11"""Terminal User Interface for container monitoring."""
22
3- import sys
43import time
54import curses
65from typing import Optional , List
7- from data_collector import ContainerDataCollector , CgroupInfo , ContainerStats
6+ from data_collection import ContainerDataCollector
87
98
109class ContainerMonitorTUI :
@@ -122,11 +121,11 @@ def _draw_selection_screen(self, stdscr):
122121 # Highlight selected
123122 stdscr .attron (curses .color_pair (2 ) | curses .A_BOLD | curses .A_REVERSE )
124123 line = f"► { cgroup .name :<40} ID: { cgroup .id } "
125- stdscr .addstr (y , 2 , line [:width - 4 ])
124+ stdscr .addstr (y , 2 , line [: width - 4 ])
126125 stdscr .attroff (curses .color_pair (2 ) | curses .A_BOLD | curses .A_REVERSE )
127126 else :
128127 line = f" { cgroup .name :<40} ID: { cgroup .id } "
129- stdscr .addstr (y , 2 , line [:width - 4 ])
128+ stdscr .addstr (y , 2 , line [: width - 4 ])
130129
131130 # Footer with count
132131 footer = f"Total cgroups: { len (cgroups )} "
@@ -174,43 +173,75 @@ def _draw_monitoring_screen(self, stdscr):
174173
175174 # RX graph
176175 y += 2
177- rx_label = f"RX: { self ._format_bytes (stats .rx_bytes )} ({ stats .rx_packets :,} packets)"
176+ rx_label = (
177+ f"RX: { self ._format_bytes (stats .rx_bytes )} ({ stats .rx_packets :,} packets)"
178+ )
178179 stdscr .addstr (y , 2 , rx_label )
179180 if len (history ) > 1 :
180- self ._draw_bar_graph (stdscr , y + 1 , 2 , width - 4 , 3 ,
181- [s .rx_bytes for s in history ],
182- curses .color_pair (2 ))
181+ self ._draw_bar_graph (
182+ stdscr ,
183+ y + 1 ,
184+ 2 ,
185+ width - 4 ,
186+ 3 ,
187+ [s .rx_bytes for s in history ],
188+ curses .color_pair (2 ),
189+ )
183190
184191 # TX graph
185192 y += 5
186- tx_label = f"TX: { self ._format_bytes (stats .tx_bytes )} ({ stats .tx_packets :,} packets)"
193+ tx_label = (
194+ f"TX: { self ._format_bytes (stats .tx_bytes )} ({ stats .tx_packets :,} packets)"
195+ )
187196 stdscr .addstr (y , 2 , tx_label )
188197 if len (history ) > 1 :
189- self ._draw_bar_graph (stdscr , y + 1 , 2 , width - 4 , 3 ,
190- [s .tx_bytes for s in history ],
191- curses .color_pair (3 ))
198+ self ._draw_bar_graph (
199+ stdscr ,
200+ y + 1 ,
201+ 2 ,
202+ width - 4 ,
203+ 3 ,
204+ [s .tx_bytes for s in history ],
205+ curses .color_pair (3 ),
206+ )
192207
193208 # File I/O graphs
194209 y += 5
195210 self ._draw_section_header (stdscr , y , "FILE I/O" , 1 )
196211
197212 # Read graph
198213 y += 2
199- read_label = f"READ: { self ._format_bytes (stats .read_bytes )} ({ stats .read_ops :,} ops)"
214+ read_label = (
215+ f"READ: { self ._format_bytes (stats .read_bytes )} ({ stats .read_ops :,} ops)"
216+ )
200217 stdscr .addstr (y , 2 , read_label )
201218 if len (history ) > 1 :
202- self ._draw_bar_graph (stdscr , y + 1 , 2 , width - 4 , 3 ,
203- [s .read_bytes for s in history ],
204- curses .color_pair (4 ))
219+ self ._draw_bar_graph (
220+ stdscr ,
221+ y + 1 ,
222+ 2 ,
223+ width - 4 ,
224+ 3 ,
225+ [s .read_bytes for s in history ],
226+ curses .color_pair (4 ),
227+ )
205228
206229 # Write graph
207230 y += 5
208- write_label = f"WRITE: { self ._format_bytes (stats .write_bytes )} ({ stats .write_ops :,} ops)"
231+ write_label = (
232+ f"WRITE: { self ._format_bytes (stats .write_bytes )} ({ stats .write_ops :,} ops)"
233+ )
209234 stdscr .addstr (y , 2 , write_label )
210235 if len (history ) > 1 :
211- self ._draw_bar_graph (stdscr , y + 1 , 2 , width - 4 , 3 ,
212- [s .write_bytes for s in history ],
213- curses .color_pair (5 ))
236+ self ._draw_bar_graph (
237+ stdscr ,
238+ y + 1 ,
239+ 2 ,
240+ width - 4 ,
241+ 3 ,
242+ [s .write_bytes for s in history ],
243+ curses .color_pair (5 ),
244+ )
214245
215246 def _draw_section_header (self , stdscr , y : int , title : str , color_pair : int ):
216247 """Draw a section header."""
@@ -220,8 +251,16 @@ def _draw_section_header(self, stdscr, y: int, title: str, color_pair: int):
220251 stdscr .addstr (y , len (title ) + 3 , "─" * (width - len (title ) - 5 ))
221252 stdscr .attroff (curses .color_pair (color_pair ) | curses .A_BOLD )
222253
223- def _draw_bar_graph (self , stdscr , y : int , x : int , width : int , height : int ,
224- data : List [float ], color_pair : int ):
254+ def _draw_bar_graph (
255+ self ,
256+ stdscr ,
257+ y : int ,
258+ x : int ,
259+ width : int ,
260+ height : int ,
261+ data : List [float ],
262+ color_pair : int ,
263+ ):
225264 """Draw a simple bar graph."""
226265 if not data or width < 2 :
227266 return
@@ -250,25 +289,21 @@ def _draw_bar_graph(self, stdscr, y: int, x: int, width: int, height: int,
250289 else :
251290 bar_line += " "
252291
253- try :
254- stdscr .attron (color_pair )
255- stdscr .addstr (y + row , x , bar_line [:width ])
256- stdscr .attroff (color_pair )
257- except :
258- pass # Ignore errors at screen edges
292+ stdscr .attron (color_pair )
293+ stdscr .addstr (y + row , x , bar_line [:width ])
294+ stdscr .attroff (color_pair )
259295
260- def _format_bytes (self , bytes_val : int ) -> str :
296+ def _format_bytes (self , bytes_val : float ) -> str :
261297 """Format bytes into human-readable string."""
262- for unit in ['B' , 'KB' , 'MB' , 'GB' , 'TB' ]:
298+ for unit in ["B" , "KB" , "MB" , "GB" , "TB" ]:
263299 if bytes_val < 1024.0 :
264300 return f"{ bytes_val :.2f} { unit } "
265- bytes_val /= 1024.
266- 0
301+ bytes_val /= 1024.0
267302 return f"{ bytes_val :.2f} PB"
268303
269304 def _handle_input (self , key : int ) -> bool :
270305 """Handle keyboard input. Returns False to exit."""
271- if key == ord ('q' ) or key == ord ('Q' ):
306+ if key == ord ("q" ) or key == ord ("Q" ):
272307 return False # Exit
273308
274309 if self .current_screen == "selection" :
@@ -277,19 +312,19 @@ def _handle_input(self, key: int) -> bool:
277312 elif key == curses .KEY_DOWN :
278313 cgroups = self .collector .get_all_cgroups ()
279314 self .selected_index = min (len (cgroups ) - 1 , self .selected_index + 1 )
280- elif key == ord (' \n ' ) or key == curses .KEY_ENTER or key == 10 :
315+ elif key == ord (" \n " ) or key == curses .KEY_ENTER or key == 10 :
281316 # Select cgroup
282317 cgroups = self .collector .get_all_cgroups ()
283318 if cgroups and 0 <= self .selected_index < len (cgroups ):
284319 cgroups .sort (key = lambda c : c .name )
285320 self .selected_cgroup = cgroups [self .selected_index ].id
286321 self .current_screen = "monitoring"
287- elif key == ord ('r' ) or key == ord ('R' ):
322+ elif key == ord ("r" ) or key == ord ("R" ):
288323 # Force refresh cache
289324 self .collector ._cgroup_cache_time = 0
290325
291326 elif self .current_screen == "monitoring" :
292- if key == 27 or key == ord ('b' ) or key == ord ('B' ): # ESC or 'b'
327+ if key == 27 or key == ord ("b" ) or key == ord ("B" ): # ESC or 'b'
293328 self .current_screen = "selection"
294329 self .selected_cgroup = None
295330
0 commit comments