@@ -54,7 +54,7 @@ def __init__(self, amp_edit: QLineEdit, parent=None):
5454 # Public API
5555 # ------------------------------------------------------------------
5656
57- def update_cathodes (self , cathode_labels : list [str ]) -> None :
57+ def update_cathodes (self , cathode_labels : list [str ], is_single_grouped_directional : bool = False ) -> None :
5858 """Rebuild the rows to match the current set of active cathodes.
5959
6060 Called by the parent view whenever the electrode canvas changes.
@@ -68,8 +68,10 @@ def update_cathodes(self, cathode_labels: list[str]) -> None:
6868 # Filter out "case" — CASE as cathode does not get a percentage row
6969 cathode_labels = [lbl for lbl in cathode_labels if lbl .lower () != "case" ]
7070
71- if len (cathode_labels ) <= 1 :
72- # Single or no cathode → hide everything
71+ # Use the passed parameter to determine if this is a single grouped directional contact
72+
73+ # Hide widget if no cathodes, or single non-grouped cathode
74+ if len (cathode_labels ) == 0 or (len (cathode_labels ) == 1 and not is_single_grouped_directional ):
7375 self ._clear_rows ()
7476 self ._cathode_labels = []
7577 self ._percentages .clear ()
@@ -86,7 +88,11 @@ def update_cathodes(self, cathode_labels: list[str]) -> None:
8688
8789 # Compute new default percentages
8890 self ._cathode_labels = list (cathode_labels )
89- self ._redistribute_percentages ()
91+ if len (cathode_labels ) == 1 and is_single_grouped_directional :
92+ # For single grouped directional contact, set 100% to enable segment splitting
93+ self ._percentages [cathode_labels [0 ]] = 100.0
94+ else :
95+ self ._redistribute_percentages ()
9096
9197 # Rebuild UI rows
9298 self ._rebuild_rows ()
@@ -104,9 +110,12 @@ def get_amplitude_text(self) -> str:
104110 e.g. ``"1.5_1.0"``.
105111 """
106112 total_text = self ._amp_edit .text ().strip ()
113+
114+ # For single or no cathode, return total amplitude
107115 if len (self ._cathode_labels ) <= 1 :
108116 return total_text
109117
118+ # Multiple cathodes - use regular logic
110119 try :
111120 total_amp = float (total_text )
112121 except (ValueError , TypeError ):
@@ -151,29 +160,102 @@ def _rebuild_rows(self) -> None:
151160 """Tear down and recreate all percentage rows."""
152161 self ._clear_rows ()
153162
154- for i , lbl in enumerate (self ._cathode_labels ):
163+ # Check if we have a single grouped contact (E1, E2, etc.)
164+ if (len (self ._cathode_labels ) == 1 and
165+ len (self ._cathode_labels [0 ]) >= 2 and
166+ self ._cathode_labels [0 ][0 ] == 'E' and
167+ self ._cathode_labels [0 ][1 :].isdigit ()):
168+ # Create segment rows for grouped contact
169+ self ._create_segment_rows_only (self ._cathode_labels [0 ])
170+ else :
171+ # Create regular rows for individual contacts/segments
172+ for i , lbl in enumerate (self ._cathode_labels ):
173+ self ._create_regular_row (lbl , i )
174+
175+ self ._refresh_ma_values ()
176+
177+ def _create_regular_row (self , lbl : str , index : int ) -> None :
178+ """Create a regular percentage row for a single contact."""
179+ row_w = QWidget ()
180+ row_layout = QHBoxLayout (row_w )
181+ row_layout .setContentsMargins (20 , 0 , 0 , 0 )
182+ row_layout .setSpacing (4 )
183+
184+ # Contact label
185+ name_lbl = QLabel (f"<b>{ lbl } </b>" )
186+ name_lbl .setMinimumWidth (40 )
187+ row_layout .addWidget (name_lbl )
188+
189+ # Percentage edit
190+ pct_edit = QLineEdit ()
191+ pct_edit .setMaximumWidth (55 )
192+ pct_edit .setAlignment (Qt .AlignRight )
193+ pct_edit .setValidator (QDoubleValidator (0.0 , 100.0 , 1 ))
194+ pct_edit .setText (f"{ self ._percentages .get (lbl , 0.0 ):g} " )
195+ row_layout .addWidget (pct_edit )
196+
197+ pct_label = QLabel ("%" )
198+ row_layout .addWidget (pct_label )
199+
200+ arrow_lbl = QLabel ("->" )
201+ row_layout .addWidget (arrow_lbl )
202+
203+ # Computed mA label (read-only)
204+ ma_label = QLabel ("" )
205+ ma_label .setMinimumWidth (60 )
206+ ma_label .setStyleSheet ("color: #64748b;" )
207+ row_layout .addWidget (ma_label )
208+
209+ row_layout .addStretch (1 )
210+
211+ is_last = (index == len (self ._cathode_labels ) - 1 )
212+
213+ # Connect editing
214+ if is_last :
215+ pct_edit .setReadOnly (True )
216+ pct_edit .setStyleSheet ("background: transparent; border: none; color: #64748b;" )
217+ else :
218+ pct_edit .editingFinished .connect (
219+ lambda lbl_ = lbl : self ._on_pct_edited (lbl_ )
220+ )
221+ pct_edit .textChanged .connect (
222+ lambda _text , lbl_ = lbl : self ._on_pct_text_changed (lbl_ )
223+ )
224+
225+ self ._rows [lbl ] = (row_w , pct_edit , ma_label )
226+ self ._layout .addWidget (row_w )
227+
228+ def _create_segment_rows_only (self , parent_lbl : str ) -> None :
229+ """Create only segment rows for a grouped directional contact (no main row)."""
230+ seg_labels = ["a" , "b" , "c" ]
231+ parent_percentage = self ._percentages .get (parent_lbl , 0.0 )
232+ segment_percentage = round (parent_percentage / 3.0 , 1 ) # Equal split by default, rounded to 1 decimal
233+
234+ for seg_idx , seg_label in enumerate (seg_labels ):
235+ seg_lbl = f"{ parent_lbl } { seg_label } "
236+
155237 row_w = QWidget ()
156238 row_layout = QHBoxLayout (row_w )
157- row_layout .setContentsMargins (20 , 0 , 0 , 0 )
239+ row_layout .setContentsMargins (20 , 0 , 0 , 0 ) # Regular indentation (not extra)
158240 row_layout .setSpacing (4 )
159241
160- # Contact label
161- name_lbl = QLabel (f"<b>{ lbl } </b>" )
242+ # Segment label
243+ name_lbl = QLabel (f"<b>{ seg_lbl } </b>" )
162244 name_lbl .setMinimumWidth (40 )
163245 row_layout .addWidget (name_lbl )
164246
165- # Percentage edit
247+ # Percentage edit (editable for segments)
166248 pct_edit = QLineEdit ()
167249 pct_edit .setMaximumWidth (55 )
168250 pct_edit .setAlignment (Qt .AlignRight )
169251 pct_edit .setValidator (QDoubleValidator (0.0 , 100.0 , 1 ))
170- pct_edit .setText (f"{ self . _percentages . get ( lbl , 0.0 ) :g} " )
252+ pct_edit .setText (f"{ segment_percentage :g} " )
171253 row_layout .addWidget (pct_edit )
172254
173255 pct_label = QLabel ("%" )
174256 row_layout .addWidget (pct_label )
175257
176- arrow_lbl = QLabel ("→ " )
258+ arrow_lbl = QLabel ("-> " )
177259 row_layout .addWidget (arrow_lbl )
178260
179261 # Computed mA label (read-only)
@@ -184,24 +266,25 @@ def _rebuild_rows(self) -> None:
184266
185267 row_layout .addStretch (1 )
186268
187- is_last = (i == len (self ._cathode_labels ) - 1 )
269+ # Store segment percentages
270+ self ._percentages [seg_lbl ] = segment_percentage
188271
189- # Connect editing
272+ # Connect editing for segment rows
273+ is_last = (seg_idx == len (seg_labels ) - 1 )
190274 if is_last :
191275 pct_edit .setReadOnly (True )
192276 pct_edit .setStyleSheet ("background: transparent; border: none; color: #64748b;" )
193277 else :
194278 pct_edit .editingFinished .connect (
195- lambda lbl_ = lbl : self ._on_pct_edited (lbl_ )
279+ lambda lbl_ = seg_lbl : self ._on_pct_edited (lbl_ )
196280 )
197281 pct_edit .textChanged .connect (
198- lambda _text , lbl_ = lbl : self ._on_pct_text_changed (lbl_ )
282+ lambda _text , lbl_ = seg_lbl : self ._on_pct_text_changed (lbl_ )
199283 )
200284
201- self ._rows [lbl ] = (row_w , pct_edit , ma_label )
285+ self ._rows [seg_lbl ] = (row_w , pct_edit , ma_label )
202286 self ._layout .addWidget (row_w )
203287
204- self ._refresh_ma_values ()
205288
206289 def _on_pct_edited (self , edited_label : str ) -> None :
207290 """Called when user finishes editing a percentage field."""
@@ -266,7 +349,8 @@ def _refresh_ma_values(self) -> None:
266349 except (ValueError , TypeError ):
267350 total_amp = 0.0
268351
269- for lbl in self ._cathode_labels :
352+ # Update all rows (both main contacts and segments)
353+ for lbl in list (self ._rows .keys ()):
270354 row_data = self ._rows .get (lbl )
271355 if not row_data :
272356 continue
@@ -351,18 +435,30 @@ def get_cathode_labels(canvas) -> list[str]:
351435
352436 if model .is_directional :
353437 for contact_idx in range (model .num_contacts ):
354- seg_states = [
355- canvas .contact_states .get ((contact_idx , seg ), ContactState .OFF )
356- for seg in range (3 )
357- ]
358- if all (s == ContactState .CATHODIC for s in seg_states ):
359- labels .append (f"E{ contact_idx } " )
360- continue
361- seg_labels = ["a" , "b" , "c" ]
362- for seg , seg_state in enumerate (seg_states ):
363- if seg_state == ContactState .CATHODIC :
364- labels .append (f"E{ contact_idx } { seg_labels [seg ]} " )
438+ # Check if this contact level is directional (has segments)
439+ is_contact_directional = model .is_level_directional (contact_idx )
440+
441+ if is_contact_directional :
442+ # This contact has segments - check individual segments
443+ seg_states = [
444+ canvas .contact_states .get ((contact_idx , seg ), ContactState .OFF )
445+ for seg in range (3 )
446+ ]
447+ if all (s == ContactState .CATHODIC for s in seg_states ):
448+ # All segments are cathodic - show as ring contact (no segment suffix)
449+ labels .append (f"E{ contact_idx } " )
450+ continue
451+ seg_labels = ["a" , "b" , "c" ]
452+ for seg , seg_state in enumerate (seg_states ):
453+ if seg_state == ContactState .CATHODIC :
454+ labels .append (f"E{ contact_idx } { seg_labels [seg ]} " )
455+ else :
456+ # This contact is a ring contact - no segments
457+ state = canvas .contact_states .get ((contact_idx , 0 ), ContactState .OFF )
458+ if state == ContactState .CATHODIC :
459+ labels .append (f"E{ contact_idx } " )
365460 else :
461+ # Non-directional model - all contacts are ring contacts
366462 for contact_idx in range (model .num_contacts ):
367463 state = canvas .contact_states .get ((contact_idx , 0 ), ContactState .OFF )
368464 if state == ContactState .CATHODIC :
0 commit comments