1010import yaml
1111
1212from PySide6 .QtCore import QProcess , QTimer
13- from PySide6 .QtWidgets import QApplication , QFileDialog , QMessageBox
13+ from PySide6 .QtWidgets import QApplication
1414
1515from ..WidgetsCreator import ProgressDialog
1616from Ui .i18n import log_i18n_level , log_with_level
@@ -64,6 +64,9 @@ def __init__(self, parent_widget):
6464 self .venv_check_progress = None
6565 self .progress_dialog = None
6666
67+ # UI delegate callbacks — registered by VenvManagerUI (Ui layer)
68+ self ._ui_callbacks : dict = {}
69+
6770 # Internal timers to enforce timeouts on background processes
6871 self ._proc_timers : list [QTimer ] = []
6972
@@ -85,6 +88,17 @@ def __init__(self, parent_widget):
8588 # User-driven cancellation flag for long-running async flows
8689 self ._cancel_requested = False
8790
91+ # ---------- UI delegate ----------
92+ def _call_ui (self , method : str , * args , ** kwargs ):
93+ """Invoke a registered UI callback by name. Returns None if no delegate registered."""
94+ fn = self ._ui_callbacks .get (method )
95+ if callable (fn ):
96+ try :
97+ return fn (* args , ** kwargs )
98+ except Exception :
99+ pass
100+ return None
101+
88102 # ---------- Workspace pref (.ark/pref.json) ----------
89103 def _workspace_pref_path (self , workspace_dir : str ) -> str :
90104 """Return the resolved workspace path information."""
@@ -162,19 +176,8 @@ def apply_workspace_pref(self, workspace_dir: str) -> bool:
162176 setattr (self .parent , "venv_path" , None )
163177 except Exception :
164178 pass
165- try :
166- if hasattr (self .parent , "venv_label" ) and self .parent .venv_label :
167- self .parent .venv_label .setText (self ._pref_label_system ())
168- except Exception :
169- pass
170- try :
171- if (
172- hasattr (self .parent , "venv_path_edit" )
173- and self .parent .venv_path_edit
174- ):
175- self .parent .venv_path_edit .setText (self ._pref_label_system ())
176- except Exception :
177- pass
179+ self ._call_ui ("update_venv_label" , self ._pref_label_system ())
180+ self ._call_ui ("update_venv_path_edit" , self ._pref_label_system ())
178181 return True
179182 if mode == "venv" and isinstance (venv_path , str ) and venv_path :
180183 venv_path = os .path .abspath (venv_path )
@@ -193,24 +196,8 @@ def apply_workspace_pref(self, workspace_dir: str) -> bool:
193196 setattr (self .parent , "venv_path" , venv_path )
194197 except Exception :
195198 pass
196- try :
197- if (
198- hasattr (self .parent , "venv_label" )
199- and self .parent .venv_label
200- ):
201- self .parent .venv_label .setText (
202- f"Venv sélectionné : { venv_path } "
203- )
204- except Exception :
205- pass
206- try :
207- if (
208- hasattr (self .parent , "venv_path_edit" )
209- and self .parent .venv_path_edit
210- ):
211- self .parent .venv_path_edit .setText (venv_path )
212- except Exception :
213- pass
199+ self ._call_ui ("update_venv_label" , f"Venv sélectionné : { venv_path } " )
200+ self ._call_ui ("update_venv_path_edit" , venv_path )
214201 return True
215202 self ._clear_workspace_pref (workspace_dir )
216203 return False
@@ -877,58 +864,35 @@ def _safe_mkdir(self, path: str) -> bool:
877864 return False
878865
879866 def _prompt_recreate_invalid_venv (self , venv_root : str , reason : str ) -> bool :
880- """Show an English message box explaining the invalid venv and propose deletion/recreation.
881- Returns True if user accepted to recreate, False otherwise.
867+ """Ask the UI delegate whether to delete and recreate an invalid venv.
868+ If the user confirms, performs deletion then triggers recreation (business logic).
869+ Returns True if recreation was initiated, False otherwise.
882870 """
871+ confirmed = self ._call_ui ("ask_recreate_invalid_venv" , venv_root , reason )
872+ if not confirmed :
873+ return False
874+ # Business logic: delete the bad venv
883875 try :
884- title = "Environnement virtuel invalide / Invalid virtual environment"
885- folder = os .path .basename (os .path .normpath (venv_root ))
886- msg = (
887- "L'environnement virtuel du workspace est invalide :\n "
888- f"- { reason } \n \n "
889- f"Voulez-vous supprimer le dossier '{ folder } ' et le recréer ?\n \n "
890- "The workspace virtual environment is invalid:\n "
891- f"- { reason } \n \n "
892- f"Do you want to delete the '{ folder } ' folder and recreate it?"
893- )
894- reply = QMessageBox .question (
895- self .parent ,
896- title ,
897- msg ,
898- QMessageBox .Yes | QMessageBox .No ,
899- QMessageBox .No ,
876+ shutil .rmtree (venv_root )
877+ self ._safe_log (f"🗑️ Deleted invalid venv: { venv_root } " )
878+ except Exception as e :
879+ self ._call_ui (
880+ "show_error_dialog" ,
881+ "Environnement virtuel invalide / Invalid virtual environment" ,
882+ f"Échec suppression venv / Failed to delete venv: { e } " ,
900883 )
901- if reply == QMessageBox .Yes :
902- try :
903- shutil .rmtree (venv_root )
904- self ._safe_log (f"🗑️ Deleted invalid venv: { venv_root } " )
905- except Exception as e :
906- try :
907- QMessageBox .critical (
908- self .parent ,
909- title ,
910- f"Échec suppression venv / Failed to delete venv: { e } " ,
911- )
912- except Exception :
913- pass
914- return False
915- # Recreate fresh venv under the workspace
916- try :
917- workspace_dir = os .path .dirname (venv_root )
918- self .create_venv_if_needed (workspace_dir )
919- return True
920- except Exception as e :
921- try :
922- QMessageBox .critical (
923- self .parent ,
924- title ,
925- f"Échec de recréation du venv / Failed to recreate venv: { e } " ,
926- )
927- except Exception :
928- pass
929- return False
930884 return False
931- except Exception :
885+ # Business logic: recreate
886+ try :
887+ workspace_dir = os .path .dirname (venv_root )
888+ self .create_venv_if_needed (workspace_dir )
889+ return True
890+ except Exception as e :
891+ self ._call_ui (
892+ "show_error_dialog" ,
893+ "Environnement virtuel invalide / Invalid virtual environment" ,
894+ f"Échec de recréation du venv / Failed to recreate venv: { e } " ,
895+ )
932896 return False
933897
934898 # ---------- Venv validation ----------
@@ -1248,28 +1212,7 @@ def _apply_system_python(self) -> None:
12481212 self .parent .venv_path_manuel = None
12491213 except Exception :
12501214 pass
1251- try :
1252- if hasattr (self .parent , "venv_label" ) and self .parent .venv_label :
1253- label = None
1254- try :
1255- tr = (
1256- getattr (self .parent , "_tr" , {})
1257- if hasattr (self .parent , "_tr" )
1258- else {}
1259- )
1260- label = (
1261- tr .get ("venv_label_system" ) if isinstance (tr , dict ) else None
1262- )
1263- except Exception :
1264- label = None
1265- if not label :
1266- label = self .parent .tr (
1267- "Venv sélectionné : Python système" ,
1268- "Venv selected: System Python" ,
1269- )
1270- self .parent .venv_label .setText (label )
1271- except Exception :
1272- pass
1215+ self ._call_ui ("update_venv_label" , self ._pref_label_system ())
12731216 try :
12741217 self ._safe_log ("✅ Utilisation de Python système pour la compilation." )
12751218 except Exception :
@@ -1280,148 +1223,6 @@ def _apply_system_python(self) -> None:
12801223 except Exception :
12811224 pass
12821225
1283- # ---------- Manual selection ----------
1284- def select_venv_manually (self ):
1285- """Execute select_venv_manually logic for this component."""
1286- try :
1287- ok_sys , missing , has_source = self ._can_use_system_python ()
1288- if ok_sys :
1289- title = self .parent .tr ("Suggestion de venv" , "Venv suggestion" )
1290- if has_source :
1291- msg = self .parent .tr (
1292- "Python système contient les dépendances nécessaires.\n "
1293- "Souhaitez-vous l'utiliser ?" ,
1294- "System Python has the required dependencies.\n "
1295- "Do you want to use it?" ,
1296- )
1297- else :
1298- msg = self .parent .tr (
1299- "Aucun fichier de dépendances détecté.\n "
1300- "Souhaitez-vous utiliser Python système ?" ,
1301- "No dependency file detected.\n "
1302- "Do you want to use System Python?" ,
1303- )
1304- reply = QMessageBox .question (
1305- self .parent , title , msg , QMessageBox .Yes | QMessageBox .No
1306- )
1307- if reply == QMessageBox .Yes :
1308- self ._apply_system_python ()
1309- return
1310- else :
1311- if missing :
1312- try :
1313- self ._safe_log (
1314- "ℹ️ Python système incomplet: "
1315- + ", " .join (sorted (set (missing )))
1316- )
1317- except Exception :
1318- pass
1319- except Exception :
1320- pass
1321-
1322- folder = QFileDialog .getExistingDirectory (
1323- self .parent ,
1324- self .parent .tr ("Choisir un dossier venv" , "Choose a venv folder" ),
1325- "" ,
1326- )
1327- if folder :
1328- path = os .path .abspath (folder )
1329- ok , reason = self .validate_venv_strict (path )
1330- if ok :
1331- try :
1332- setattr (self .parent , "use_system_python" , False )
1333- except Exception :
1334- pass
1335- self .parent .venv_path_manuel = path
1336- if hasattr (self .parent , "venv_label" ) and self .parent .venv_label :
1337- self .parent .venv_label .setText (f"Venv sélectionné : { path } " )
1338- self ._safe_log (f"✅ Venv valide sélectionné: { path } " )
1339- try :
1340- workspace_dir = getattr (self .parent , "workspace_dir" , None )
1341- self .save_workspace_pref (workspace_dir )
1342- except Exception :
1343- pass
1344- else :
1345- self ._safe_log (f"❌ Venv refusé: { reason } " )
1346- self .parent .venv_path_manuel = None
1347- try :
1348- setattr (self .parent , "use_system_python" , False )
1349- except Exception :
1350- pass
1351- if hasattr (self .parent , "venv_label" ) and self .parent .venv_label :
1352- self .parent .venv_label .setText ("Venv sélectionné : Aucun" )
1353- try :
1354- workspace_dir = getattr (self .parent , "workspace_dir" , None )
1355- self .save_workspace_pref (workspace_dir )
1356- except Exception :
1357- pass
1358- # Message concis avec actions proposées
1359- try :
1360-
1361- def _t (_key : str , fr : str , en : str ) -> str :
1362- """Execute _t logic for this component."""
1363- try :
1364- return self .parent .tr (fr , en )
1365- except Exception :
1366- return en
1367-
1368- box = QMessageBox (self .parent )
1369- box .setWindowTitle (
1370- _t ("msg_invalid_venv_title" , "Venv invalide" , "Invalid Venv" )
1371- )
1372- box .setText (
1373- _t (
1374- "msg_invalid_venv_text" ,
1375- "Le dossier sélectionné n'est pas un venv valide. Réessayer ou créer un venv ?" ,
1376- "The selected folder is not a valid venv. Retry or create a venv?" ,
1377- )
1378- )
1379- if reason :
1380- try :
1381- box .setInformativeText (str (reason ))
1382- except Exception :
1383- pass
1384- btn_retry = box .addButton (
1385- _t ("action_retry" , "Réessayer" , "Retry" ),
1386- QMessageBox .AcceptRole ,
1387- )
1388- btn_create = None
1389- workspace_dir = getattr (self .parent , "workspace_dir" , None )
1390- if workspace_dir :
1391- btn_create = box .addButton (
1392- _t ("action_create_venv" , "Créer un venv" , "Create venv" ),
1393- QMessageBox .ActionRole ,
1394- )
1395- box .addButton (
1396- _t ("action_cancel" , "Annuler" , "Cancel" ),
1397- QMessageBox .RejectRole ,
1398- )
1399- box .exec ()
1400- if box .clickedButton () == btn_retry :
1401- self .select_venv_manually ()
1402- return
1403- if btn_create and box .clickedButton () == btn_create :
1404- try :
1405- self .create_venv_if_needed (workspace_dir )
1406- except Exception :
1407- pass
1408- return
1409- except Exception :
1410- pass
1411- else :
1412- self .parent .venv_path_manuel = None
1413- try :
1414- setattr (self .parent , "use_system_python" , False )
1415- except Exception :
1416- pass
1417- if hasattr (self .parent , "venv_label" ) and self .parent .venv_label :
1418- self .parent .venv_label .setText ("Venv sélectionné : Aucun" )
1419- try :
1420- workspace_dir = getattr (self .parent , "workspace_dir" , None )
1421- self .save_workspace_pref (workspace_dir )
1422- except Exception :
1423- pass
1424-
14251226 # ---------- Existing venv: check and install tools ----------
14261227 def check_tools_in_venv (self , venv_path : str ):
14271228 """Execute check_tools_in_venv logic for this component."""
@@ -2072,13 +1873,9 @@ def _on_venv_created(self, process, code, status, venv_path):
20721873 if not getattr (self .parent , "use_system_python" , False ):
20731874 if not getattr (self .parent , "venv_path_manuel" , None ):
20741875 self .parent .venv_path_manuel = venv_path
2075- if (
2076- hasattr (self .parent , "venv_label" )
2077- and self .parent .venv_label
2078- ):
2079- self .parent .venv_label .setText (
2080- f"Venv sélectionné : { venv_path } "
2081- )
1876+ self ._call_ui (
1877+ "update_venv_label" , f"Venv sélectionné : { venv_path } "
1878+ )
20821879 self .save_workspace_pref (os .path .dirname (venv_path ))
20831880 except Exception :
20841881 pass
0 commit comments