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
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.
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.
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.
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.
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.