|
33 | 33 |
|
34 | 34 | import yaml |
35 | 35 |
|
36 | | -from PySide6.QtCore import Qt, Signal, QMimeData, QByteArray |
| 36 | +from PySide6.QtCore import Qt, Signal, QMimeData, QByteArray, QObject, QThread, Slot |
37 | 37 | from PySide6.QtGui import QColor, QShortcut, QKeySequence, QPalette |
38 | 38 | from PySide6.QtWidgets import ( |
39 | 39 | QAbstractItemView, |
@@ -772,6 +772,160 @@ def _do_save(self) -> None: |
772 | 772 | ) |
773 | 773 |
|
774 | 774 |
|
| 775 | +# --------------------------------------------------------------------------- |
| 776 | +# Worker et bridge Qt (déplacés depuis bcasl/Loader.py) |
| 777 | +# --------------------------------------------------------------------------- |
| 778 | + |
| 779 | + |
| 780 | +class _BCASLWorker(QObject): |
| 781 | + finished = Signal(object) # report or None |
| 782 | + log = Signal(str) |
| 783 | + |
| 784 | + def __init__( |
| 785 | + self, |
| 786 | + workspace_root: "Path", |
| 787 | + Plugins_dir: "Path", |
| 788 | + cfg: "dict[str, Any]", |
| 789 | + plugin_timeout: float, |
| 790 | + ) -> None: |
| 791 | + super().__init__() |
| 792 | + self.workspace_root = workspace_root |
| 793 | + self.Plugins_dir = Plugins_dir |
| 794 | + self.cfg = cfg |
| 795 | + self.plugin_timeout = plugin_timeout |
| 796 | + self._cancel_requested = False |
| 797 | + |
| 798 | + def request_cancel(self) -> None: |
| 799 | + try: |
| 800 | + self._cancel_requested = True |
| 801 | + except Exception: |
| 802 | + pass |
| 803 | + |
| 804 | + @Slot() |
| 805 | + def run(self) -> None: |
| 806 | + try: |
| 807 | + from bcasl.Loader import _run_bcasl_sync |
| 808 | + |
| 809 | + report = _run_bcasl_sync( |
| 810 | + self.workspace_root, |
| 811 | + self.Plugins_dir, |
| 812 | + self.cfg, |
| 813 | + self.plugin_timeout, |
| 814 | + log_cb=self.log.emit, |
| 815 | + stop_requested=lambda: bool(self._cancel_requested), |
| 816 | + ) |
| 817 | + self.finished.emit(report) |
| 818 | + except Exception as e: |
| 819 | + try: |
| 820 | + self.log.emit(f"Erreur BCASL: {e}\n") |
| 821 | + except Exception: |
| 822 | + pass |
| 823 | + self.finished.emit(None) |
| 824 | + |
| 825 | + |
| 826 | +class _BCASLUiBridge(QObject): |
| 827 | + def __init__(self, gui, on_done, thread) -> None: |
| 828 | + super().__init__() |
| 829 | + self._gui = gui |
| 830 | + self._on_done = on_done |
| 831 | + self._thread = thread |
| 832 | + |
| 833 | + @Slot(str) |
| 834 | + def on_log(self, s: str) -> None: |
| 835 | + try: |
| 836 | + if hasattr(self._gui, "log") and self._gui.log: |
| 837 | + self._gui.log.append(s) |
| 838 | + except Exception: |
| 839 | + pass |
| 840 | + |
| 841 | + @Slot(object) |
| 842 | + def on_finished(self, rep) -> None: |
| 843 | + try: |
| 844 | + if rep and hasattr(self._gui, "log") and self._gui.log is not None: |
| 845 | + self._gui.log.append("BCASL - Rapport:\n") |
| 846 | + for item in rep: |
| 847 | + try: |
| 848 | + state = ( |
| 849 | + "OK" |
| 850 | + if getattr(item, "success", False) |
| 851 | + else f"FAIL: {getattr(item, 'error', '')}" |
| 852 | + ) |
| 853 | + dur = getattr(item, "duration_ms", 0.0) |
| 854 | + pid = getattr(item, "plugin_id", "?") |
| 855 | + self._gui.log.append(f" - {pid}: {state} ({dur:.1f} ms)\n") |
| 856 | + except Exception: |
| 857 | + pass |
| 858 | + try: |
| 859 | + self._gui.log.append(rep.summary() + "\n") |
| 860 | + except Exception: |
| 861 | + pass |
| 862 | + try: |
| 863 | + if callable(self._on_done): |
| 864 | + self._on_done(rep) |
| 865 | + except Exception: |
| 866 | + pass |
| 867 | + finally: |
| 868 | + try: |
| 869 | + self._thread.quit() |
| 870 | + except Exception: |
| 871 | + pass |
| 872 | + |
| 873 | + |
| 874 | +def _build_plugin_item( |
| 875 | + pid: str, |
| 876 | + meta: "dict[str, Any]", |
| 877 | + plugins_cfg: "dict[str, Any]", |
| 878 | + Qt, |
| 879 | + QListWidgetItem, |
| 880 | +) -> Any: |
| 881 | + """Construit un QListWidgetItem pour un plugin BCASL.""" |
| 882 | + from bcasl.tagging import get_tag_phase_name |
| 883 | + |
| 884 | + label = meta.get("name") or pid |
| 885 | + ver = meta.get("version") or "" |
| 886 | + tags = meta.get("tags") or [] |
| 887 | + |
| 888 | + phase_name = get_tag_phase_name(tags[0]) if tags else "" |
| 889 | + text = f"{label} ({pid})" + (f" v{ver}" if ver else "") |
| 890 | + if phase_name: |
| 891 | + text += f" [Phase: {phase_name}]" |
| 892 | + |
| 893 | + item = QListWidgetItem(text) |
| 894 | + |
| 895 | + try: |
| 896 | + desc = meta.get("description") or "" |
| 897 | + tooltip = desc |
| 898 | + if tags: |
| 899 | + tooltip += f"\n\nTags: {', '.join(tags)}" |
| 900 | + reqs = meta.get("requirements", []) |
| 901 | + if reqs: |
| 902 | + tooltip += "\n\nRequirements:\n" + "\n".join(f" • {req}" for req in reqs) |
| 903 | + if tooltip: |
| 904 | + item.setToolTip(tooltip) |
| 905 | + except Exception: |
| 906 | + pass |
| 907 | + |
| 908 | + from bcasl.Loader import _plugin_enabled |
| 909 | + |
| 910 | + enabled = _plugin_enabled(plugins_cfg, pid) |
| 911 | + try: |
| 912 | + item.setData(0x0100, pid) |
| 913 | + except Exception: |
| 914 | + pass |
| 915 | + if Qt is not None: |
| 916 | + item.setFlags( |
| 917 | + item.flags() |
| 918 | + | Qt.ItemIsUserCheckable |
| 919 | + | Qt.ItemIsEnabled |
| 920 | + | Qt.ItemIsSelectable |
| 921 | + | Qt.ItemIsDragEnabled |
| 922 | + ) |
| 923 | + item.setCheckState( |
| 924 | + Qt.CheckState.Checked if enabled else Qt.CheckState.Unchecked |
| 925 | + ) |
| 926 | + return item |
| 927 | + |
| 928 | + |
775 | 929 | # --------------------------------------------------------------------------- |
776 | 930 | # Point d'entrée (appelé depuis bcasl/Loader.py) |
777 | 931 | # --------------------------------------------------------------------------- |
|
0 commit comments