1010
1111import json
1212import os
13- import queue
1413import re
1514import subprocess
1615import sys
@@ -436,42 +435,18 @@ def run(self, cmd: list[str], on_done: callable | None = None) -> None:
436435 self .write (f"$ { display_cmd } \n " ,
437436 color = ft .Colors .with_opacity (0.40 , ft .Colors .WHITE ))
438437
439- # Line queue: (text, color). _flush_thread drains it every 50 ms.
440- _line_q : queue .Queue = queue .Queue ()
441- _FLUSH_INTERVAL = 0.05 # seconds between UI updates
442-
443- def _flush_thread () -> None :
444- """Drain the line queue and do a single page.update() per interval."""
445- default_color = ft .Colors .with_opacity (0.88 , ft .Colors .WHITE )
446- while True :
447- batch : list [tuple [str , str | None ]] = []
448- try :
449- # Block until the first item arrives
450- batch .append (_line_q .get (timeout = 1.0 ))
451- except queue .Empty :
452- if not state .running :
453- break
454- continue
455- # Drain everything else that arrived in this window
456- deadline = time .monotonic () + _FLUSH_INTERVAL
457- while time .monotonic () < deadline :
458- try :
459- batch .append (_line_q .get_nowait ())
460- except queue .Empty :
461- break
462- # Append all lines, one page.update() for the whole batch
463- for text , color in batch :
464- for line in text .splitlines ():
465- self ._lines .controls .append (
466- ft .Text (
467- line , size = 11 , font_family = MONO ,
468- color = color or default_color ,
469- no_wrap = False , selectable = True ,
470- )
471- )
472- self ._lines .update () # push the ListView itself
473- self .page .update ()
474- time .sleep (0.02 ) # yield to Flet event loop so repaint fires
438+ _UPDATE_INTERVAL = 0.1 # seconds between UI refreshes while streaming
439+ _default_color = ft .Colors .with_opacity (0.88 , ft .Colors .WHITE )
440+
441+ def _append_line (text : str , color : str | None = None ) -> None :
442+ for line in text .splitlines ():
443+ self ._lines .controls .append (
444+ ft .Text (
445+ line , size = 11 , font_family = MONO ,
446+ color = color or _default_color ,
447+ no_wrap = False , selectable = True ,
448+ )
449+ )
475450
476451 def _worker () -> None :
477452 try :
@@ -485,8 +460,9 @@ def _worker() -> None:
485460 bufsize = 1 ,
486461 env = env ,
487462 )
463+ _last_update = time .monotonic ()
464+ _pending = False
488465 for line in state .proc .stdout :
489- # Filter only the noisiest frozen-importlib bootstrap lines
490466 stripped = line .strip ()
491467 if stripped .startswith ("<frozen importlib" ):
492468 continue
@@ -496,30 +472,38 @@ def _worker() -> None:
496472 color = C_ERROR
497473 elif low .startswith ("warning" ):
498474 color = C_WARN
499- _line_q .put ((line .rstrip (), color ))
475+ _append_line (line .rstrip (), color )
476+ _pending = True
477+ now = time .monotonic ()
478+ if now - _last_update >= _UPDATE_INTERVAL :
479+ self .page .update ()
480+ _last_update = now
481+ _pending = False
482+
483+ if _pending :
484+ self .page .update ()
485+
500486 state .proc .wait ()
501487 rc = state .proc .returncode
502488
503489 if rc == 0 :
504- _line_q . put (( "\n ✓ Completed successfully." , C_SUCCESS ) )
490+ _append_line ( "\n ✓ Completed successfully." , C_SUCCESS )
505491 self .set_status ("✓ done" , C_SUCCESS )
506- elif rc == - 15 : # SIGTERM from our stop button
507- pass # already handled in _on_stop
492+ elif rc == - 15 :
493+ pass
508494 else :
509- _line_q . put (( f"\n ✗ Exited with code { rc } ." , C_ERROR ) )
495+ _append_line ( f"\n ✗ Exited with code { rc } ." , C_ERROR )
510496 self .set_status (f"✗ code { rc } " , C_ERROR )
511497 except Exception as exc :
512- _line_q . put (( f"\n ✗ { exc } " , C_ERROR ) )
498+ _append_line ( f"\n ✗ { exc } " , C_ERROR )
513499 self .set_status ("✗ error" , C_ERROR )
514500 finally :
515- state .running = False
516- state .proc = None
501+ state .running = False
502+ state .proc = None
517503 self ._stop_btn .visible = False
518504 self .page .update ()
519505 if on_done :
520506 on_done ()
521-
522- threading .Thread (target = _flush_thread , daemon = True ).start ()
523507 threading .Thread (target = _worker , daemon = True ).start ()
524508
525509# ── UI helpers ────────────────────────────────────────────────────────────────
0 commit comments