valve_changes_window

This module defines the ValveChanges class, a Tkinter Toplevel window designed to act as a confirmation dialog window for auto-generated valve timing adjustments.

It presents the user with a summary of proposed changes to valve open durations before they are applied to the system configuration. The user can either confirm the changes, triggering a callback function to views.valve_testing.valve_testing_window, or abort the changes, closing the dialog without further action. It relies on system_config for valve configuration and views.gui_common.GUIUtils for standard widget creation and window management.

  1"""
  2This module defines the ValveChanges class, a Tkinter Toplevel window designed
  3to act as a confirmation dialog window for auto-generated valve timing adjustments.
  4
  5It presents the user with a summary of proposed changes to valve open durations
  6before they are applied to the system configuration.
  7The user can either confirm the changes, triggering a callback function to `views.valve_testing.valve_testing_window`, or
  8abort the changes, closing the dialog without further action. It relies on
  9`system_config` for valve configuration and `views.gui_common.GUIUtils` for
 10standard widget creation and window management.
 11"""
 12
 13import tkinter as tk
 14import toml
 15import system_config
 16
 17###TYPE HINTS###
 18from typing import Callable
 19###TYPE HINTS###
 20
 21from views.gui_common import GUIUtils
 22
 23
 24rig_config = system_config.get_rig_config()
 25
 26with open(rig_config, "r") as f:
 27    VALVE_CONFIG = toml.load(f)["valve_config"]
 28
 29# pull total valves constant from toml config
 30TOTAL_VALVES = VALVE_CONFIG["TOTAL_CURRENT_VALVES"]
 31
 32VALVES_PER_SIDE = TOTAL_VALVES // 2
 33
 34
 35# lambda: self.confirm_valve_changes(side_one, side_two, side_one_old, side_two_old
 36class ValveChanges(tk.Toplevel):
 37    """
 38    Implements a Tkinter Toplevel window to display and confirm proposed valve duration changes.
 39
 40    This window acts as a confirmation step, showing the user which valves are
 41    being changed and their old vs. new duration values (in microseconds).
 42    It provides "Confirm Changes" and "ABORT CHANGES" buttons. Confirming
 43    triggers a callback function passed during initialization; aborting simply
 44    destroys the window. Uses `views.gui_common.GUIUtils` for window centering and icon setting.
 45
 46    Attributes
 47    ----------
 48    - **`valve_changes`** (*List[Tuple[int, Tuple[int, int]]]*): A list where each element is a tuple containing the valve number (int)
 49        and another tuple with (old_duration, new_duration).
 50    - **`confirm_callback`** (*Callable*): The function to execute when the "Confirm Changes" button is pressed.
 51    - **`valve_frame`** (*tk.Frame*): The main container frame holding the side-by-side valve change displays.
 52    - **`side_one_valves_frame`** (*tk.Frame*): Frame displaying changes for valves on the first side (e.g., 1-8).
 53    - **`side_two_valves_frame`** (*tk.Frame*): Frame displaying changes for valves on the second side (e.g., 9-16).
 54    - **`buttons_frame`** (*tk.Frame*): Frame containing the Confirm and Abort buttons.
 55
 56    Methods
 57    -------
 58    - `create_interface`()
 59        Constructs the main GUI layout, including frames and buttons.
 60    - `confirmation`()
 61        Executes the stored callback function and then destroys the window.
 62    - `create_valve_change_labels`()
 63        Populates the side frames with labels detailing the old and new duration for each changed valve.
 64    """
 65
 66    def __init__(
 67        self,
 68        valve_changes: list[tuple[int, tuple[int, int]]],
 69        confirm_callback: Callable,
 70    ):
 71        """
 72        Initializes the ValveChanges confirmation Toplevel window.
 73
 74        Parameters
 75        ----------
 76        - **valve_changes** (*List[Tuple[int, Tuple[int, int]]]*): A list containing tuples for each valve change.
 77          Each inner tuple has the format `(valve_number, (old_duration_microseconds, new_duration_microseconds))`.
 78        - **confirm_callback** (*Callable*): The function that should be called if the user confirms the changes.
 79          This function takes no arguments.
 80
 81        Raises
 82        ------
 83        - Propagates exceptions from `GUIUtils` methods during icon setting or window centering.
 84        """
 85        super().__init__()
 86        self.title("CONFIRM VALVE DURATION CHANGES")
 87        self.bind("<Control-w>", lambda event: self.withdraw())
 88
 89        self.grid_columnconfigure(0, weight=1)
 90        self.grid_columnconfigure(1, weight=1)
 91
 92        self.grid_rowconfigure(0, weight=1)
 93
 94        self.resizable(False, False)
 95
 96        self.valve_changes = valve_changes
 97        self.confirm_callback = confirm_callback
 98
 99        self.create_interface()
100
101        self.update_idletasks()
102        GUIUtils.center_window(self)
103
104        window_icon_path = GUIUtils.get_window_icon_path()
105        GUIUtils.set_program_icon(self, icon_path=window_icon_path)
106
107    def create_interface(self):
108        """
109        Constructs the main GUI elements within the Toplevel window.
110
111        Creates the frames to hold the valve change information side-by-side,
112        populates them using `create_valve_change_labels`, and adds the
113        'Confirm' and 'Abort' buttons.
114        """
115
116        self.valve_frame = tk.Frame(
117            self, highlightbackground="black", highlightthickness=1
118        )
119
120        self.valve_frame.grid(row=0, column=0, pady=10, padx=10, sticky="nsew")
121
122        self.valve_frame.grid_columnconfigure(0, weight=1)
123        self.valve_frame.grid_columnconfigure(1, weight=1)
124
125        self.valve_frame.grid_rowconfigure(0, weight=1)
126
127        self.side_one_valves_frame = tk.Frame(
128            self.valve_frame, highlightbackground="black", highlightthickness=1
129        )
130        self.side_one_valves_frame.grid(row=0, column=0, pady=10, padx=10, sticky="ew")
131
132        self.side_two_valves_frame = tk.Frame(
133            self.valve_frame, highlightbackground="black", highlightthickness=1
134        )
135        self.side_two_valves_frame.grid(row=0, column=1, pady=10, padx=10, sticky="ew")
136
137        for i in range(2):
138            self.side_one_valves_frame.grid_columnconfigure(i, weight=1)
139            self.side_two_valves_frame.grid_columnconfigure(i, weight=1)
140
141        for i in range(8):
142            self.side_one_valves_frame.grid_rowconfigure(i, weight=1)
143            self.side_two_valves_frame.grid_rowconfigure(i, weight=1)
144
145        self.create_valve_change_labels()
146
147        self.buttons_frame = tk.Frame(
148            self, highlightbackground="black", highlightthickness=1
149        )
150        self.buttons_frame.grid(row=1, column=0, pady=10, padx=10, sticky="ew")
151        self.buttons_frame.grid_rowconfigure(0, weight=1)
152        self.buttons_frame.grid_columnconfigure(0, weight=1)
153        self.buttons_frame.grid_columnconfigure(1, weight=1)
154
155        confirm = tk.Button(
156            self.buttons_frame,
157            text="Confirm Changes",
158            command=lambda: self.confirmation(),
159            bg="green",
160            font=("Helvetica", 20),
161            width=20,
162        )
163        confirm.grid(row=0, column=0, sticky="w", padx=5, ipadx=10, ipady=10)
164
165        abort = tk.Button(
166            self.buttons_frame,
167            text="ABORT CHANGES",
168            command=lambda: self.destroy(),
169            bg="tomato",
170            font=("Helvetica", 20),
171            width=20,
172        )
173        abort.grid(row=0, column=1, sticky="e", padx=5, ipadx=10, ipady=10)
174
175    def confirmation(self):
176        """
177        Handles the confirmation action.
178
179        Executes the `confirm_callback` function that was provided during
180        class initialization and then closes (destroys) this confirmation window.
181        """
182        self.confirm_callback()
183        self.destroy()
184
185    def create_valve_change_labels(self):
186        """
187        Generates and places the labels detailing each valve duration change.
188
189        Iterates through the `self.valve_changes` list. For each change, it
190        determines which side frame (left or right) the valve belongs to and
191        creates two labels: one for the valve number and one showing the
192        'From old_duration --> new_duration' information. Labels are placed
193        in sequence within the appropriate side frame.
194        """
195
196        valve = None
197        frame = None
198        side_one_row = 0
199        side_two_row = 0
200        column = 0
201
202        for valve, durations in self.valve_changes:
203            valve = valve
204            old_duration = durations[0]
205            new_duration = durations[1]
206
207            if valve <= VALVES_PER_SIDE:
208                frame = self.side_one_valves_frame
209                row = side_one_row
210                column = 0
211            else:
212                frame = self.side_two_valves_frame
213                row = side_two_row
214                column = 1
215
216            label = tk.Label(
217                frame,
218                text=f"Valve {valve}",
219                bg="grey",
220                font=("Helvetica", 24),
221                highlightthickness=1,
222                highlightbackground="dark blue",
223                height=3,
224                width=26,
225            )
226            label.grid(row=row, column=column, padx=10, pady=10, sticky="nsew")
227
228            label = tk.Label(
229                frame,
230                text=f"From {old_duration} --> {new_duration}",
231                bg="light blue",
232                font=("Helvetica", 24),
233                highlightthickness=1,
234                highlightbackground="dark blue",
235                height=3,
236                width=26,
237            )
238            label.grid(row=row + 1, column=column, padx=10, pady=10, sticky="nsew")
239
240            if valve <= VALVES_PER_SIDE:
241                side_one_row += 2
242            else:
243                side_two_row += 2
rig_config = '/home/blake/Documents/Photologic-Experiment-Rig-Files/assets/rig_config.toml'
TOTAL_VALVES = 8
VALVES_PER_SIDE = 4
class ValveChanges(tkinter.Toplevel):
 37class ValveChanges(tk.Toplevel):
 38    """
 39    Implements a Tkinter Toplevel window to display and confirm proposed valve duration changes.
 40
 41    This window acts as a confirmation step, showing the user which valves are
 42    being changed and their old vs. new duration values (in microseconds).
 43    It provides "Confirm Changes" and "ABORT CHANGES" buttons. Confirming
 44    triggers a callback function passed during initialization; aborting simply
 45    destroys the window. Uses `views.gui_common.GUIUtils` for window centering and icon setting.
 46
 47    Attributes
 48    ----------
 49    - **`valve_changes`** (*List[Tuple[int, Tuple[int, int]]]*): A list where each element is a tuple containing the valve number (int)
 50        and another tuple with (old_duration, new_duration).
 51    - **`confirm_callback`** (*Callable*): The function to execute when the "Confirm Changes" button is pressed.
 52    - **`valve_frame`** (*tk.Frame*): The main container frame holding the side-by-side valve change displays.
 53    - **`side_one_valves_frame`** (*tk.Frame*): Frame displaying changes for valves on the first side (e.g., 1-8).
 54    - **`side_two_valves_frame`** (*tk.Frame*): Frame displaying changes for valves on the second side (e.g., 9-16).
 55    - **`buttons_frame`** (*tk.Frame*): Frame containing the Confirm and Abort buttons.
 56
 57    Methods
 58    -------
 59    - `create_interface`()
 60        Constructs the main GUI layout, including frames and buttons.
 61    - `confirmation`()
 62        Executes the stored callback function and then destroys the window.
 63    - `create_valve_change_labels`()
 64        Populates the side frames with labels detailing the old and new duration for each changed valve.
 65    """
 66
 67    def __init__(
 68        self,
 69        valve_changes: list[tuple[int, tuple[int, int]]],
 70        confirm_callback: Callable,
 71    ):
 72        """
 73        Initializes the ValveChanges confirmation Toplevel window.
 74
 75        Parameters
 76        ----------
 77        - **valve_changes** (*List[Tuple[int, Tuple[int, int]]]*): A list containing tuples for each valve change.
 78          Each inner tuple has the format `(valve_number, (old_duration_microseconds, new_duration_microseconds))`.
 79        - **confirm_callback** (*Callable*): The function that should be called if the user confirms the changes.
 80          This function takes no arguments.
 81
 82        Raises
 83        ------
 84        - Propagates exceptions from `GUIUtils` methods during icon setting or window centering.
 85        """
 86        super().__init__()
 87        self.title("CONFIRM VALVE DURATION CHANGES")
 88        self.bind("<Control-w>", lambda event: self.withdraw())
 89
 90        self.grid_columnconfigure(0, weight=1)
 91        self.grid_columnconfigure(1, weight=1)
 92
 93        self.grid_rowconfigure(0, weight=1)
 94
 95        self.resizable(False, False)
 96
 97        self.valve_changes = valve_changes
 98        self.confirm_callback = confirm_callback
 99
100        self.create_interface()
101
102        self.update_idletasks()
103        GUIUtils.center_window(self)
104
105        window_icon_path = GUIUtils.get_window_icon_path()
106        GUIUtils.set_program_icon(self, icon_path=window_icon_path)
107
108    def create_interface(self):
109        """
110        Constructs the main GUI elements within the Toplevel window.
111
112        Creates the frames to hold the valve change information side-by-side,
113        populates them using `create_valve_change_labels`, and adds the
114        'Confirm' and 'Abort' buttons.
115        """
116
117        self.valve_frame = tk.Frame(
118            self, highlightbackground="black", highlightthickness=1
119        )
120
121        self.valve_frame.grid(row=0, column=0, pady=10, padx=10, sticky="nsew")
122
123        self.valve_frame.grid_columnconfigure(0, weight=1)
124        self.valve_frame.grid_columnconfigure(1, weight=1)
125
126        self.valve_frame.grid_rowconfigure(0, weight=1)
127
128        self.side_one_valves_frame = tk.Frame(
129            self.valve_frame, highlightbackground="black", highlightthickness=1
130        )
131        self.side_one_valves_frame.grid(row=0, column=0, pady=10, padx=10, sticky="ew")
132
133        self.side_two_valves_frame = tk.Frame(
134            self.valve_frame, highlightbackground="black", highlightthickness=1
135        )
136        self.side_two_valves_frame.grid(row=0, column=1, pady=10, padx=10, sticky="ew")
137
138        for i in range(2):
139            self.side_one_valves_frame.grid_columnconfigure(i, weight=1)
140            self.side_two_valves_frame.grid_columnconfigure(i, weight=1)
141
142        for i in range(8):
143            self.side_one_valves_frame.grid_rowconfigure(i, weight=1)
144            self.side_two_valves_frame.grid_rowconfigure(i, weight=1)
145
146        self.create_valve_change_labels()
147
148        self.buttons_frame = tk.Frame(
149            self, highlightbackground="black", highlightthickness=1
150        )
151        self.buttons_frame.grid(row=1, column=0, pady=10, padx=10, sticky="ew")
152        self.buttons_frame.grid_rowconfigure(0, weight=1)
153        self.buttons_frame.grid_columnconfigure(0, weight=1)
154        self.buttons_frame.grid_columnconfigure(1, weight=1)
155
156        confirm = tk.Button(
157            self.buttons_frame,
158            text="Confirm Changes",
159            command=lambda: self.confirmation(),
160            bg="green",
161            font=("Helvetica", 20),
162            width=20,
163        )
164        confirm.grid(row=0, column=0, sticky="w", padx=5, ipadx=10, ipady=10)
165
166        abort = tk.Button(
167            self.buttons_frame,
168            text="ABORT CHANGES",
169            command=lambda: self.destroy(),
170            bg="tomato",
171            font=("Helvetica", 20),
172            width=20,
173        )
174        abort.grid(row=0, column=1, sticky="e", padx=5, ipadx=10, ipady=10)
175
176    def confirmation(self):
177        """
178        Handles the confirmation action.
179
180        Executes the `confirm_callback` function that was provided during
181        class initialization and then closes (destroys) this confirmation window.
182        """
183        self.confirm_callback()
184        self.destroy()
185
186    def create_valve_change_labels(self):
187        """
188        Generates and places the labels detailing each valve duration change.
189
190        Iterates through the `self.valve_changes` list. For each change, it
191        determines which side frame (left or right) the valve belongs to and
192        creates two labels: one for the valve number and one showing the
193        'From old_duration --> new_duration' information. Labels are placed
194        in sequence within the appropriate side frame.
195        """
196
197        valve = None
198        frame = None
199        side_one_row = 0
200        side_two_row = 0
201        column = 0
202
203        for valve, durations in self.valve_changes:
204            valve = valve
205            old_duration = durations[0]
206            new_duration = durations[1]
207
208            if valve <= VALVES_PER_SIDE:
209                frame = self.side_one_valves_frame
210                row = side_one_row
211                column = 0
212            else:
213                frame = self.side_two_valves_frame
214                row = side_two_row
215                column = 1
216
217            label = tk.Label(
218                frame,
219                text=f"Valve {valve}",
220                bg="grey",
221                font=("Helvetica", 24),
222                highlightthickness=1,
223                highlightbackground="dark blue",
224                height=3,
225                width=26,
226            )
227            label.grid(row=row, column=column, padx=10, pady=10, sticky="nsew")
228
229            label = tk.Label(
230                frame,
231                text=f"From {old_duration} --> {new_duration}",
232                bg="light blue",
233                font=("Helvetica", 24),
234                highlightthickness=1,
235                highlightbackground="dark blue",
236                height=3,
237                width=26,
238            )
239            label.grid(row=row + 1, column=column, padx=10, pady=10, sticky="nsew")
240
241            if valve <= VALVES_PER_SIDE:
242                side_one_row += 2
243            else:
244                side_two_row += 2

Implements a Tkinter Toplevel window to display and confirm proposed valve duration changes.

This window acts as a confirmation step, showing the user which valves are being changed and their old vs. new duration values (in microseconds). It provides "Confirm Changes" and "ABORT CHANGES" buttons. Confirming triggers a callback function passed during initialization; aborting simply destroys the window. Uses views.gui_common.GUIUtils for window centering and icon setting.

Attributes

  • valve_changes (List[Tuple[int, Tuple[int, int]]]): A list where each element is a tuple containing the valve number (int) and another tuple with (old_duration, new_duration).
  • confirm_callback (Callable): The function to execute when the "Confirm Changes" button is pressed.
  • valve_frame (tk.Frame): The main container frame holding the side-by-side valve change displays.
  • side_one_valves_frame (tk.Frame): Frame displaying changes for valves on the first side (e.g., 1-8).
  • side_two_valves_frame (tk.Frame): Frame displaying changes for valves on the second side (e.g., 9-16).
  • buttons_frame (tk.Frame): Frame containing the Confirm and Abort buttons.

Methods

  • create_interface() Constructs the main GUI layout, including frames and buttons.
  • confirmation() Executes the stored callback function and then destroys the window.
  • create_valve_change_labels() Populates the side frames with labels detailing the old and new duration for each changed valve.
ValveChanges( valve_changes: list[tuple[int, tuple[int, int]]], confirm_callback: Callable)
 67    def __init__(
 68        self,
 69        valve_changes: list[tuple[int, tuple[int, int]]],
 70        confirm_callback: Callable,
 71    ):
 72        """
 73        Initializes the ValveChanges confirmation Toplevel window.
 74
 75        Parameters
 76        ----------
 77        - **valve_changes** (*List[Tuple[int, Tuple[int, int]]]*): A list containing tuples for each valve change.
 78          Each inner tuple has the format `(valve_number, (old_duration_microseconds, new_duration_microseconds))`.
 79        - **confirm_callback** (*Callable*): The function that should be called if the user confirms the changes.
 80          This function takes no arguments.
 81
 82        Raises
 83        ------
 84        - Propagates exceptions from `GUIUtils` methods during icon setting or window centering.
 85        """
 86        super().__init__()
 87        self.title("CONFIRM VALVE DURATION CHANGES")
 88        self.bind("<Control-w>", lambda event: self.withdraw())
 89
 90        self.grid_columnconfigure(0, weight=1)
 91        self.grid_columnconfigure(1, weight=1)
 92
 93        self.grid_rowconfigure(0, weight=1)
 94
 95        self.resizable(False, False)
 96
 97        self.valve_changes = valve_changes
 98        self.confirm_callback = confirm_callback
 99
100        self.create_interface()
101
102        self.update_idletasks()
103        GUIUtils.center_window(self)
104
105        window_icon_path = GUIUtils.get_window_icon_path()
106        GUIUtils.set_program_icon(self, icon_path=window_icon_path)

Initializes the ValveChanges confirmation Toplevel window.

Parameters

  • valve_changes (List[Tuple[int, Tuple[int, int]]]): A list containing tuples for each valve change. Each inner tuple has the format (valve_number, (old_duration_microseconds, new_duration_microseconds)).
  • confirm_callback (Callable): The function that should be called if the user confirms the changes. This function takes no arguments.

Raises

  • Propagates exceptions from GUIUtils methods during icon setting or window centering.
valve_changes
confirm_callback
def create_interface(self):
108    def create_interface(self):
109        """
110        Constructs the main GUI elements within the Toplevel window.
111
112        Creates the frames to hold the valve change information side-by-side,
113        populates them using `create_valve_change_labels`, and adds the
114        'Confirm' and 'Abort' buttons.
115        """
116
117        self.valve_frame = tk.Frame(
118            self, highlightbackground="black", highlightthickness=1
119        )
120
121        self.valve_frame.grid(row=0, column=0, pady=10, padx=10, sticky="nsew")
122
123        self.valve_frame.grid_columnconfigure(0, weight=1)
124        self.valve_frame.grid_columnconfigure(1, weight=1)
125
126        self.valve_frame.grid_rowconfigure(0, weight=1)
127
128        self.side_one_valves_frame = tk.Frame(
129            self.valve_frame, highlightbackground="black", highlightthickness=1
130        )
131        self.side_one_valves_frame.grid(row=0, column=0, pady=10, padx=10, sticky="ew")
132
133        self.side_two_valves_frame = tk.Frame(
134            self.valve_frame, highlightbackground="black", highlightthickness=1
135        )
136        self.side_two_valves_frame.grid(row=0, column=1, pady=10, padx=10, sticky="ew")
137
138        for i in range(2):
139            self.side_one_valves_frame.grid_columnconfigure(i, weight=1)
140            self.side_two_valves_frame.grid_columnconfigure(i, weight=1)
141
142        for i in range(8):
143            self.side_one_valves_frame.grid_rowconfigure(i, weight=1)
144            self.side_two_valves_frame.grid_rowconfigure(i, weight=1)
145
146        self.create_valve_change_labels()
147
148        self.buttons_frame = tk.Frame(
149            self, highlightbackground="black", highlightthickness=1
150        )
151        self.buttons_frame.grid(row=1, column=0, pady=10, padx=10, sticky="ew")
152        self.buttons_frame.grid_rowconfigure(0, weight=1)
153        self.buttons_frame.grid_columnconfigure(0, weight=1)
154        self.buttons_frame.grid_columnconfigure(1, weight=1)
155
156        confirm = tk.Button(
157            self.buttons_frame,
158            text="Confirm Changes",
159            command=lambda: self.confirmation(),
160            bg="green",
161            font=("Helvetica", 20),
162            width=20,
163        )
164        confirm.grid(row=0, column=0, sticky="w", padx=5, ipadx=10, ipady=10)
165
166        abort = tk.Button(
167            self.buttons_frame,
168            text="ABORT CHANGES",
169            command=lambda: self.destroy(),
170            bg="tomato",
171            font=("Helvetica", 20),
172            width=20,
173        )
174        abort.grid(row=0, column=1, sticky="e", padx=5, ipadx=10, ipady=10)

Constructs the main GUI elements within the Toplevel window.

Creates the frames to hold the valve change information side-by-side, populates them using create_valve_change_labels, and adds the 'Confirm' and 'Abort' buttons.

def confirmation(self):
176    def confirmation(self):
177        """
178        Handles the confirmation action.
179
180        Executes the `confirm_callback` function that was provided during
181        class initialization and then closes (destroys) this confirmation window.
182        """
183        self.confirm_callback()
184        self.destroy()

Handles the confirmation action.

Executes the confirm_callback function that was provided during class initialization and then closes (destroys) this confirmation window.

def create_valve_change_labels(self):
186    def create_valve_change_labels(self):
187        """
188        Generates and places the labels detailing each valve duration change.
189
190        Iterates through the `self.valve_changes` list. For each change, it
191        determines which side frame (left or right) the valve belongs to and
192        creates two labels: one for the valve number and one showing the
193        'From old_duration --> new_duration' information. Labels are placed
194        in sequence within the appropriate side frame.
195        """
196
197        valve = None
198        frame = None
199        side_one_row = 0
200        side_two_row = 0
201        column = 0
202
203        for valve, durations in self.valve_changes:
204            valve = valve
205            old_duration = durations[0]
206            new_duration = durations[1]
207
208            if valve <= VALVES_PER_SIDE:
209                frame = self.side_one_valves_frame
210                row = side_one_row
211                column = 0
212            else:
213                frame = self.side_two_valves_frame
214                row = side_two_row
215                column = 1
216
217            label = tk.Label(
218                frame,
219                text=f"Valve {valve}",
220                bg="grey",
221                font=("Helvetica", 24),
222                highlightthickness=1,
223                highlightbackground="dark blue",
224                height=3,
225                width=26,
226            )
227            label.grid(row=row, column=column, padx=10, pady=10, sticky="nsew")
228
229            label = tk.Label(
230                frame,
231                text=f"From {old_duration} --> {new_duration}",
232                bg="light blue",
233                font=("Helvetica", 24),
234                highlightthickness=1,
235                highlightbackground="dark blue",
236                height=3,
237                width=26,
238            )
239            label.grid(row=row + 1, column=column, padx=10, pady=10, sticky="nsew")
240
241            if valve <= VALVES_PER_SIDE:
242                side_one_row += 2
243            else:
244                side_two_row += 2

Generates and places the labels detailing each valve duration change.

Iterates through the self.valve_changes list. For each change, it determines which side frame (left or right) the valve belongs to and creates two labels: one for the valve number and one showing the 'From old_duration --> new_duration' information. Labels are placed in sequence within the appropriate side frame.