22
33from typing import Optional
44from textual .app import App , ComposeResult
5- from textual .widgets import Header , Footer , Static , DirectoryTree , Checkbox , Button , ScrollView , LoadingIndicator , DataTable
5+ # ScrollView API moved between textual versions; try importing from widgets first,
6+ # then from containers, otherwise provide a minimal fallback shim so the TUI still runs.
7+ try :
8+ from textual .widgets import Header , Footer , Static , DirectoryTree , Checkbox , Button , LoadingIndicator , DataTable
9+ from textual .containers import VerticalScroll as ScrollView
10+ _SCROLL_SRC = 'textual.containers'
11+ except Exception :
12+ try :
13+ # some textual versions expose ScrollView from widgets
14+ from textual .widgets import Header , Footer , Static , DirectoryTree , Checkbox , Button , LoadingIndicator , DataTable , ScrollView
15+ _SCROLL_SRC = 'textual.widgets'
16+ except Exception :
17+ # final fallback: emulate a simple ScrollView using Container so imports succeed and children are rendered
18+ from textual .widgets import Header , Footer , Static , DirectoryTree , Checkbox , Button , LoadingIndicator , DataTable
19+ from textual .containers import Container
20+ class ScrollView (Container ): # type: ignore
21+ """Fallback ScrollView used when textual does not provide one."""
22+ pass
23+ _SCROLL_SRC = 'fallback'
624from textual .containers import Container , Horizontal , Vertical
725from textual .screen import ModalScreen
826from patternlab .engine import Engine
@@ -51,6 +69,7 @@ def compose(self) -> ComposeResult:
5169 DataTable (id = "results_table" ),
5270 Static ("Tıklanabilir sonuç listesi" , id = "results_list_label" ),
5371 ScrollView (id = "results_scroll" ),
72+ Static ("" , id = "status" , expand = False ), # Status mesajları için
5473 Horizontal (
5574 Button ("Başlat" , id = "start_btn" , variant = "success" ),
5675 Button ("Çıkış" , id = "exit_btn" , variant = "error" ),
@@ -236,17 +255,17 @@ def on_button_pressed(self, event: Button.Pressed) -> None:
236255 file_path = str (val )
237256 break
238257
239- footer = self .query_one (Footer )
240-
258+ status = self .query_one ("#status" , Static )
259+
241260 # Eğer analiz zaten çalışıyorsa yeni analiz başlatma
242261 if getattr (self , "_analysis_running" , False ):
243- footer .update ("Analiz zaten çalışıyor" )
262+ status .update ("Analiz zaten çalışıyor" )
244263 return
245-
264+
246265 # Hazırlık: Loading göster, flag set
247266 self ._analysis_running = True
248267 self ._show_loading ()
249- footer .update (f"Analiz başlatıldı — seçili testler: { len (selected_tests )} " )
268+ status .update (f"Analiz başlatıldı — seçili testler: { len (selected_tests )} " )
250269
251270 # Worker fonksiyonu: Engine.analyze'ı arka planda çağırır
252271 def _worker ():
@@ -267,28 +286,31 @@ def _worker():
267286
268287 # tamamlandığında çağrılacak callback
269288 def _on_done (result ):
270- # Analiz sona erdi — Loading'i gizle ve footer'ı güncelle
271289 self ._analysis_running = False
272290 self ._hide_loading ()
273291 try :
274- footer .update ("Analiz tamamlandı — sonuçlar gösteriliyor" )
275- except Exception :
276- pass
277- # Sonuçları göster
278- try :
279- self ._display_results (result or {})
280- except Exception :
281- pass
292+ if isinstance (result , dict ) and "error" in result :
293+ status .update (f"Analiz hatası: { result ['error' ]} " )
294+ else :
295+ status .update ("Analiz tamamlandı — sonuçlar gösteriliyor" )
296+ self ._display_results (result or {})
297+ except Exception as e :
298+ status .update (f"Görüntüleme hatası: { e } " )
282299
283- # run_worker ile arka plan çalıştır; callback ile tamamlandığında UI güncellenecek
300+ # Standart threading ile arka plan çalıştır
284301 try :
285- # Textual'ın run_worker API'sini kullanıyoruz
286- self .run_worker (_worker , callback = _on_done )
287- except Exception :
288- # Eğer run_worker erişilemezse, flag'i temizle ve Loading'i gizle
302+ import threading
303+ def _thread_worker ():
304+ result = _worker ()
305+ # Callback'i main thread'de çalıştır
306+ self .call_from_thread (_on_done , result )
307+ thread = threading .Thread (target = _thread_worker , daemon = True )
308+ thread .start ()
309+ except Exception as e :
310+ # Hata durumunda temizle
289311 self ._analysis_running = False
290312 self ._hide_loading ()
291- footer .update ("Analiz başlatılamadı (run_worker mevcut değil) " )
313+ status .update (f "Analiz başlatılamadı: { e } " )
292314 return
293315
294316 # Modal kapatma düğmesi
@@ -328,7 +350,15 @@ def __init__(self, test_name: str, metrics_text: str) -> None:
328350 def compose (self ) -> ComposeResult :
329351 yield Vertical (
330352 Static (f"Metrikler — { self .test_name } " , id = "modal_title" ),
331- ScrollView (Static (self .metrics_text , id = "modal_metrics" )),
353+ (
354+ Static (self .metrics_text , id = "modal_metrics" )
355+ if _SCROLL_SRC == 'fallback'
356+ else
357+ ScrollView (self .metrics_text , id = "modal_metrics" )
358+ if _SCROLL_SRC == 'textual.widgets'
359+ else
360+ ScrollView (Static (self .metrics_text ), id = "modal_metrics" )
361+ ),
332362 Horizontal (
333363 Button ("Kapat" , id = "modal_close" , variant = "primary" ),
334364 ),
0 commit comments