gui.panels.log_panel

  1# Copyright (C) 2026  David Vaquero <pepesan@gmail.com>
  2#
  3# This program is free software: you can redistribute it and/or modify
  4# it under the terms of the GNU General Public License as published by
  5# the Free Software Foundation, either version 3 of the License, or
  6# (at your option) any later version.
  7#
  8# This program is distributed in the hope that it will be useful,
  9# but WITHOUT ANY WARRANTY; without even the implied warranty of
 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 11# GNU General Public License for more details.
 12#
 13# You should have received a copy of the GNU General Public License
 14# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 15
 16import subprocess
 17from pathlib import Path
 18
 19from PySide6.QtWidgets import (
 20    QWidget, QVBoxLayout, QHBoxLayout, QPlainTextEdit,
 21    QPushButton, QLabel, QScrollArea, QProgressBar,
 22)
 23from PySide6.QtCore import Signal, Qt
 24from PySide6.QtGui import QFont, QTextCursor
 25
 26from gui.theme import c, signal as theme_signal
 27from lib.i18n import _
 28
 29
 30class LogPanel(QWidget):
 31    collapse_toggled = Signal(bool)   # True = pide colapsar, False = pide expandir
 32
 33    def __init__(self, parent=None):
 34        super().__init__(parent)
 35        self._collapsed = False
 36        self._odp_dir: Path | None = None
 37        self._build_ui()
 38        theme_signal().changed.connect(self._on_theme_changed)
 39
 40    def _build_ui(self):
 41        layout = QVBoxLayout(self)
 42        layout.setContentsMargins(0, 0, 0, 0)
 43        layout.setSpacing(0)
 44
 45        layout.addWidget(self._make_header())
 46
 47        self._progress_bar = QProgressBar()
 48        self._progress_bar.setObjectName('build_progress')
 49        self._progress_bar.setFixedHeight(4)
 50        self._progress_bar.setTextVisible(False)
 51        self._progress_bar.setRange(0, 100)
 52        self._progress_bar.setValue(0)
 53        self._progress_bar.setVisible(False)
 54        layout.addWidget(self._progress_bar)
 55
 56        self.text_area = QPlainTextEdit()
 57        self.text_area.setObjectName('log_text')
 58        self.text_area.setReadOnly(True)
 59        self.text_area.setFont(QFont('Liberation Mono', 10))
 60        layout.addWidget(self.text_area)
 61
 62        self._results_panel = self._make_results_panel()
 63        self._results_panel.setVisible(False)
 64        layout.addWidget(self._results_panel)
 65
 66        self._refresh_log_colors()
 67
 68    # ── Header ────────────────────────────────────────────────────────────────
 69
 70    def _make_header(self) -> QWidget:
 71        header = QWidget()
 72        header.setObjectName('log_header')
 73        header.setFixedHeight(36)
 74        hlay = QHBoxLayout(header)
 75        hlay.setContentsMargins(12, 0, 8, 0)
 76        hlay.setSpacing(4)
 77
 78        self._collapse_btn = QPushButton('▼')
 79        self._collapse_btn.setObjectName('log_collapse_btn')
 80        self._collapse_btn.setToolTip(_('Colapsar consola'))
 81        self._collapse_btn.setFixedSize(24, 24)
 82        self._collapse_btn.clicked.connect(self._toggle_collapse)
 83        hlay.addWidget(self._collapse_btn)
 84
 85        lbl = QLabel(_('Consola'))
 86        lbl.setObjectName('log_title')
 87        hlay.addWidget(lbl)
 88        hlay.addStretch()
 89
 90        for label, slot in ((_('Limpiar'), self.clear_log), (_('Copiar'), self._copy)):
 91            btn = QPushButton(label)
 92            btn.setObjectName('log_btn')
 93            btn.setFixedHeight(24)
 94            btn.clicked.connect(slot)
 95            hlay.addWidget(btn)
 96
 97        return header
 98
 99    # ── Panel de resultados (tabla de ficheros generados) ─────────────────────
100
101    def _make_results_panel(self) -> QWidget:
102        panel = QWidget()
103        panel.setObjectName('results_bar')
104        vlay = QVBoxLayout(panel)
105        vlay.setContentsMargins(0, 0, 0, 0)
106        vlay.setSpacing(0)
107
108        # Cabecera: carpeta + cerrar
109        hdr = QWidget()
110        hdr.setObjectName('results_bar')
111        hdr.setFixedHeight(36)
112        hlay = QHBoxLayout(hdr)
113        hlay.setContentsMargins(12, 6, 8, 6)
114        hlay.setSpacing(8)
115
116        lbl = QLabel(_('Generados:'))
117        lbl.setObjectName('results_label')
118        hlay.addWidget(lbl)
119
120        self._btn_folder = QPushButton(_('📁  Carpeta'))
121        self._btn_folder.setObjectName('open_result_btn')
122        self._btn_folder.setToolTip(_('Abrir carpeta de salida'))
123        self._btn_folder.clicked.connect(self._open_folder)
124        hlay.addWidget(self._btn_folder)
125
126        hlay.addStretch()
127
128        btn_hide = QPushButton('✕')
129        btn_hide.setObjectName('log_btn')
130        btn_hide.setFixedSize(24, 24)
131        btn_hide.setToolTip(_('Ocultar'))
132        btn_hide.clicked.connect(lambda: self._results_panel.setVisible(False))
133        hlay.addWidget(btn_hide)
134
135        vlay.addWidget(hdr)
136
137        # Área scrollable con las filas de presentaciones
138        self._scroll_area = QScrollArea()
139        self._scroll_area.setWidgetResizable(True)
140        self._scroll_area.setHorizontalScrollBarPolicy(
141            Qt.ScrollBarPolicy.ScrollBarAlwaysOff
142        )
143        self._scroll_area.setMaximumHeight(160)
144        self._scroll_area.setFrameShape(QScrollArea.Shape.NoFrame)
145        self._scroll_area.setObjectName('results_scroll')
146
147        self._rows_widget = QWidget()
148        self._rows_widget.setObjectName('results_bar')
149        self._rows_layout = QVBoxLayout(self._rows_widget)
150        self._rows_layout.setContentsMargins(0, 2, 0, 4)
151        self._rows_layout.setSpacing(1)
152        self._rows_layout.addStretch()
153
154        self._scroll_area.setWidget(self._rows_widget)
155        vlay.addWidget(self._scroll_area)
156
157        return panel
158
159    def _clear_result_rows(self) -> None:
160        while self._rows_layout.count() > 1:   # deja el stretch final
161            item = self._rows_layout.takeAt(0)
162            if item.widget():
163                item.widget().deleteLater()
164
165    def _add_result_row(self, odp: Path, pdf: Path | None) -> None:
166        row = QWidget()
167        row.setObjectName('results_bar')
168        row.setFixedHeight(30)
169        hlay = QHBoxLayout(row)
170        hlay.setContentsMargins(12, 2, 8, 2)
171        hlay.setSpacing(6)
172
173        # Nombre del fichero (sin versión para legibilidad)
174        name_lbl = QLabel(odp.name)
175        name_lbl.setObjectName('results_label')
176        name_lbl.setToolTip(str(odp))
177        hlay.addWidget(name_lbl, 1)
178
179        btn_odp = QPushButton(_('ODP'))
180        btn_odp.setObjectName('open_result_btn')
181        btn_odp.setFixedHeight(22)
182        btn_odp.clicked.connect(lambda _, p=odp: subprocess.Popen(['xdg-open', str(p)]))
183        hlay.addWidget(btn_odp)
184
185        if pdf and pdf.exists():
186            btn_pdf = QPushButton(_('PDF'))
187            btn_pdf.setObjectName('open_result_btn')
188            btn_pdf.setFixedHeight(22)
189            btn_pdf.clicked.connect(lambda _, p=pdf: subprocess.Popen(['xdg-open', str(p)]))
190            hlay.addWidget(btn_pdf)
191
192        # Insertar antes del stretch
193        self._rows_layout.insertWidget(self._rows_layout.count() - 1, row)
194
195    # ── Colapsar/expandir ─────────────────────────────────────────────────────
196
197    def _toggle_collapse(self) -> None:
198        self._collapsed = not self._collapsed
199        if self._collapsed:
200            self._collapse_btn.setText('▲')
201            self._collapse_btn.setToolTip(_('Expandir consola'))
202            self.text_area.hide()
203            self._results_panel.hide()
204            self.setMaximumHeight(36)
205        else:
206            self._collapse_btn.setText('▼')
207            self._collapse_btn.setToolTip(_('Colapsar consola'))
208            self.text_area.show()
209            self.setMaximumHeight(16777215)
210        self.collapse_toggled.emit(self._collapsed)
211
212    # ── API pública ───────────────────────────────────────────────────────────
213
214    def set_progress(self, current: int, total: int) -> None:
215        if total <= 0:
216            self._progress_bar.setVisible(False)
217            return
218        pct = int(current * 100 / total)
219        self._progress_bar.setValue(pct)
220        self._progress_bar.setVisible(pct < 100)
221
222    def show_build_results(self, files: list) -> None:
223        """Muestra los ficheros generados. files: list[tuple[Path, Path | None]]."""
224        if not files:
225            self._results_panel.setVisible(False)
226            return
227
228        self._odp_dir = files[0][0].parent
229        self._clear_result_rows()
230        for odp, pdf in files:
231            self._add_result_row(odp, pdf)
232
233        row_h = 30
234        content_h = len(files) * (row_h + 1) + 8
235        self._scroll_area.setMaximumHeight(min(content_h, 160))
236        self._results_panel.setVisible(True)
237
238    def hide_open_buttons(self) -> None:
239        self._results_panel.setVisible(False)
240        self._progress_bar.setValue(0)
241        self._progress_bar.setVisible(False)
242
243    def append_line(self, text: str, level: str = 'info') -> None:
244        color = self._colors.get(level, self._colors['info'])
245        self.text_area.appendHtml(
246            f'<span style="color:{color};white-space:pre;">{_escape(text)}</span>'
247        )
248        self.text_area.moveCursor(QTextCursor.MoveOperation.End)
249
250    def clear_log(self) -> None:
251        self.text_area.clear()
252
253    def toPlainText(self) -> str:
254        return self.text_area.toPlainText()
255
256    # ── Internos ──────────────────────────────────────────────────────────────
257
258    def _open_folder(self) -> None:
259        if self._odp_dir and self._odp_dir.exists():
260            subprocess.Popen(['xdg-open', str(self._odp_dir)])
261
262    def _copy(self) -> None:
263        from PySide6.QtWidgets import QApplication
264        QApplication.clipboard().setText(self.text_area.toPlainText())
265
266    def _refresh_log_colors(self) -> None:
267        self._colors = {
268            'ok':    c('log_ok'),
269            'error': c('log_error'),
270            'info':  c('log_info'),
271        }
272
273    def _on_theme_changed(self) -> None:
274        self._refresh_log_colors()
275
276
277def _escape(text: str) -> str:
278    return text.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
class LogPanel(PySide6.QtWidgets.QWidget):
 31class LogPanel(QWidget):
 32    collapse_toggled = Signal(bool)   # True = pide colapsar, False = pide expandir
 33
 34    def __init__(self, parent=None):
 35        super().__init__(parent)
 36        self._collapsed = False
 37        self._odp_dir: Path | None = None
 38        self._build_ui()
 39        theme_signal().changed.connect(self._on_theme_changed)
 40
 41    def _build_ui(self):
 42        layout = QVBoxLayout(self)
 43        layout.setContentsMargins(0, 0, 0, 0)
 44        layout.setSpacing(0)
 45
 46        layout.addWidget(self._make_header())
 47
 48        self._progress_bar = QProgressBar()
 49        self._progress_bar.setObjectName('build_progress')
 50        self._progress_bar.setFixedHeight(4)
 51        self._progress_bar.setTextVisible(False)
 52        self._progress_bar.setRange(0, 100)
 53        self._progress_bar.setValue(0)
 54        self._progress_bar.setVisible(False)
 55        layout.addWidget(self._progress_bar)
 56
 57        self.text_area = QPlainTextEdit()
 58        self.text_area.setObjectName('log_text')
 59        self.text_area.setReadOnly(True)
 60        self.text_area.setFont(QFont('Liberation Mono', 10))
 61        layout.addWidget(self.text_area)
 62
 63        self._results_panel = self._make_results_panel()
 64        self._results_panel.setVisible(False)
 65        layout.addWidget(self._results_panel)
 66
 67        self._refresh_log_colors()
 68
 69    # ── Header ────────────────────────────────────────────────────────────────
 70
 71    def _make_header(self) -> QWidget:
 72        header = QWidget()
 73        header.setObjectName('log_header')
 74        header.setFixedHeight(36)
 75        hlay = QHBoxLayout(header)
 76        hlay.setContentsMargins(12, 0, 8, 0)
 77        hlay.setSpacing(4)
 78
 79        self._collapse_btn = QPushButton('▼')
 80        self._collapse_btn.setObjectName('log_collapse_btn')
 81        self._collapse_btn.setToolTip(_('Colapsar consola'))
 82        self._collapse_btn.setFixedSize(24, 24)
 83        self._collapse_btn.clicked.connect(self._toggle_collapse)
 84        hlay.addWidget(self._collapse_btn)
 85
 86        lbl = QLabel(_('Consola'))
 87        lbl.setObjectName('log_title')
 88        hlay.addWidget(lbl)
 89        hlay.addStretch()
 90
 91        for label, slot in ((_('Limpiar'), self.clear_log), (_('Copiar'), self._copy)):
 92            btn = QPushButton(label)
 93            btn.setObjectName('log_btn')
 94            btn.setFixedHeight(24)
 95            btn.clicked.connect(slot)
 96            hlay.addWidget(btn)
 97
 98        return header
 99
100    # ── Panel de resultados (tabla de ficheros generados) ─────────────────────
101
102    def _make_results_panel(self) -> QWidget:
103        panel = QWidget()
104        panel.setObjectName('results_bar')
105        vlay = QVBoxLayout(panel)
106        vlay.setContentsMargins(0, 0, 0, 0)
107        vlay.setSpacing(0)
108
109        # Cabecera: carpeta + cerrar
110        hdr = QWidget()
111        hdr.setObjectName('results_bar')
112        hdr.setFixedHeight(36)
113        hlay = QHBoxLayout(hdr)
114        hlay.setContentsMargins(12, 6, 8, 6)
115        hlay.setSpacing(8)
116
117        lbl = QLabel(_('Generados:'))
118        lbl.setObjectName('results_label')
119        hlay.addWidget(lbl)
120
121        self._btn_folder = QPushButton(_('📁  Carpeta'))
122        self._btn_folder.setObjectName('open_result_btn')
123        self._btn_folder.setToolTip(_('Abrir carpeta de salida'))
124        self._btn_folder.clicked.connect(self._open_folder)
125        hlay.addWidget(self._btn_folder)
126
127        hlay.addStretch()
128
129        btn_hide = QPushButton('✕')
130        btn_hide.setObjectName('log_btn')
131        btn_hide.setFixedSize(24, 24)
132        btn_hide.setToolTip(_('Ocultar'))
133        btn_hide.clicked.connect(lambda: self._results_panel.setVisible(False))
134        hlay.addWidget(btn_hide)
135
136        vlay.addWidget(hdr)
137
138        # Área scrollable con las filas de presentaciones
139        self._scroll_area = QScrollArea()
140        self._scroll_area.setWidgetResizable(True)
141        self._scroll_area.setHorizontalScrollBarPolicy(
142            Qt.ScrollBarPolicy.ScrollBarAlwaysOff
143        )
144        self._scroll_area.setMaximumHeight(160)
145        self._scroll_area.setFrameShape(QScrollArea.Shape.NoFrame)
146        self._scroll_area.setObjectName('results_scroll')
147
148        self._rows_widget = QWidget()
149        self._rows_widget.setObjectName('results_bar')
150        self._rows_layout = QVBoxLayout(self._rows_widget)
151        self._rows_layout.setContentsMargins(0, 2, 0, 4)
152        self._rows_layout.setSpacing(1)
153        self._rows_layout.addStretch()
154
155        self._scroll_area.setWidget(self._rows_widget)
156        vlay.addWidget(self._scroll_area)
157
158        return panel
159
160    def _clear_result_rows(self) -> None:
161        while self._rows_layout.count() > 1:   # deja el stretch final
162            item = self._rows_layout.takeAt(0)
163            if item.widget():
164                item.widget().deleteLater()
165
166    def _add_result_row(self, odp: Path, pdf: Path | None) -> None:
167        row = QWidget()
168        row.setObjectName('results_bar')
169        row.setFixedHeight(30)
170        hlay = QHBoxLayout(row)
171        hlay.setContentsMargins(12, 2, 8, 2)
172        hlay.setSpacing(6)
173
174        # Nombre del fichero (sin versión para legibilidad)
175        name_lbl = QLabel(odp.name)
176        name_lbl.setObjectName('results_label')
177        name_lbl.setToolTip(str(odp))
178        hlay.addWidget(name_lbl, 1)
179
180        btn_odp = QPushButton(_('ODP'))
181        btn_odp.setObjectName('open_result_btn')
182        btn_odp.setFixedHeight(22)
183        btn_odp.clicked.connect(lambda _, p=odp: subprocess.Popen(['xdg-open', str(p)]))
184        hlay.addWidget(btn_odp)
185
186        if pdf and pdf.exists():
187            btn_pdf = QPushButton(_('PDF'))
188            btn_pdf.setObjectName('open_result_btn')
189            btn_pdf.setFixedHeight(22)
190            btn_pdf.clicked.connect(lambda _, p=pdf: subprocess.Popen(['xdg-open', str(p)]))
191            hlay.addWidget(btn_pdf)
192
193        # Insertar antes del stretch
194        self._rows_layout.insertWidget(self._rows_layout.count() - 1, row)
195
196    # ── Colapsar/expandir ─────────────────────────────────────────────────────
197
198    def _toggle_collapse(self) -> None:
199        self._collapsed = not self._collapsed
200        if self._collapsed:
201            self._collapse_btn.setText('▲')
202            self._collapse_btn.setToolTip(_('Expandir consola'))
203            self.text_area.hide()
204            self._results_panel.hide()
205            self.setMaximumHeight(36)
206        else:
207            self._collapse_btn.setText('▼')
208            self._collapse_btn.setToolTip(_('Colapsar consola'))
209            self.text_area.show()
210            self.setMaximumHeight(16777215)
211        self.collapse_toggled.emit(self._collapsed)
212
213    # ── API pública ───────────────────────────────────────────────────────────
214
215    def set_progress(self, current: int, total: int) -> None:
216        if total <= 0:
217            self._progress_bar.setVisible(False)
218            return
219        pct = int(current * 100 / total)
220        self._progress_bar.setValue(pct)
221        self._progress_bar.setVisible(pct < 100)
222
223    def show_build_results(self, files: list) -> None:
224        """Muestra los ficheros generados. files: list[tuple[Path, Path | None]]."""
225        if not files:
226            self._results_panel.setVisible(False)
227            return
228
229        self._odp_dir = files[0][0].parent
230        self._clear_result_rows()
231        for odp, pdf in files:
232            self._add_result_row(odp, pdf)
233
234        row_h = 30
235        content_h = len(files) * (row_h + 1) + 8
236        self._scroll_area.setMaximumHeight(min(content_h, 160))
237        self._results_panel.setVisible(True)
238
239    def hide_open_buttons(self) -> None:
240        self._results_panel.setVisible(False)
241        self._progress_bar.setValue(0)
242        self._progress_bar.setVisible(False)
243
244    def append_line(self, text: str, level: str = 'info') -> None:
245        color = self._colors.get(level, self._colors['info'])
246        self.text_area.appendHtml(
247            f'<span style="color:{color};white-space:pre;">{_escape(text)}</span>'
248        )
249        self.text_area.moveCursor(QTextCursor.MoveOperation.End)
250
251    def clear_log(self) -> None:
252        self.text_area.clear()
253
254    def toPlainText(self) -> str:
255        return self.text_area.toPlainText()
256
257    # ── Internos ──────────────────────────────────────────────────────────────
258
259    def _open_folder(self) -> None:
260        if self._odp_dir and self._odp_dir.exists():
261            subprocess.Popen(['xdg-open', str(self._odp_dir)])
262
263    def _copy(self) -> None:
264        from PySide6.QtWidgets import QApplication
265        QApplication.clipboard().setText(self.text_area.toPlainText())
266
267    def _refresh_log_colors(self) -> None:
268        self._colors = {
269            'ok':    c('log_ok'),
270            'error': c('log_error'),
271            'info':  c('log_info'),
272        }
273
274    def _on_theme_changed(self) -> None:
275        self._refresh_log_colors()

QWidget(self, /, parent: PySide6.QtWidgets.QWidget | None = None, f: PySide6.QtCore.Qt.WindowType = Default(Qt.WindowFlags), *, modal: bool | None = None, windowModality: PySide6.QtCore.Qt.WindowModality | None = None, enabled: bool | None = None, geometry: PySide6.QtCore.QRect | None = None, frameGeometry: PySide6.QtCore.QRect | None = None, normalGeometry: PySide6.QtCore.QRect | None = None, x: int | None = None, y: int | None = None, pos: PySide6.QtCore.QPoint | None = None, frameSize: PySide6.QtCore.QSize | None = None, size: PySide6.QtCore.QSize | None = None, width: int | None = None, height: int | None = None, rect: PySide6.QtCore.QRect | None = None, childrenRect: PySide6.QtCore.QRect | None = None, childrenRegion: PySide6.QtGui.QRegion | None = None, sizePolicy: PySide6.QtWidgets.QSizePolicy | None = None, minimumSize: PySide6.QtCore.QSize | None = None, maximumSize: PySide6.QtCore.QSize | None = None, minimumWidth: int | None = None, minimumHeight: int | None = None, maximumWidth: int | None = None, maximumHeight: int | None = None, sizeIncrement: PySide6.QtCore.QSize | None = None, baseSize: PySide6.QtCore.QSize | None = None, palette: PySide6.QtGui.QPalette | None = None, font: PySide6.QtGui.QFont | None = None, cursor: PySide6.QtGui.QCursor | None = None, mouseTracking: bool | None = None, tabletTracking: bool | None = None, isActiveWindow: bool | None = None, focusPolicy: PySide6.QtCore.Qt.FocusPolicy | None = None, focus: bool | None = None, contextMenuPolicy: PySide6.QtCore.Qt.ContextMenuPolicy | None = None, updatesEnabled: bool | None = None, visible: bool | None = None, minimized: bool | None = None, maximized: bool | None = None, fullScreen: bool | None = None, sizeHint: PySide6.QtCore.QSize | None = None, minimumSizeHint: PySide6.QtCore.QSize | None = None, acceptDrops: bool | None = None, windowTitle: str | None = None, windowIcon: PySide6.QtGui.QIcon | None = None, windowIconText: str | None = None, windowOpacity: float | None = None, windowModified: bool | None = None, toolTip: str | None = None, toolTipDuration: int | None = None, statusTip: str | None = None, whatsThis: str | None = None, accessibleName: str | None = None, accessibleDescription: str | None = None, accessibleIdentifier: str | None = None, layoutDirection: PySide6.QtCore.Qt.LayoutDirection | None = None, autoFillBackground: bool | None = None, styleSheet: str | None = None, locale: PySide6.QtCore.QLocale | None = None, windowFilePath: str | None = None, inputMethodHints: PySide6.QtCore.Qt.InputMethodHint | None = None) -> None

LogPanel(parent=None)
34    def __init__(self, parent=None):
35        super().__init__(parent)
36        self._collapsed = False
37        self._odp_dir: Path | None = None
38        self._build_ui()
39        theme_signal().changed.connect(self._on_theme_changed)
def collapse_toggled(unknown):
def set_progress(self, current: int, total: int) -> None:
215    def set_progress(self, current: int, total: int) -> None:
216        if total <= 0:
217            self._progress_bar.setVisible(False)
218            return
219        pct = int(current * 100 / total)
220        self._progress_bar.setValue(pct)
221        self._progress_bar.setVisible(pct < 100)
def show_build_results(self, files: list) -> None:
223    def show_build_results(self, files: list) -> None:
224        """Muestra los ficheros generados. files: list[tuple[Path, Path | None]]."""
225        if not files:
226            self._results_panel.setVisible(False)
227            return
228
229        self._odp_dir = files[0][0].parent
230        self._clear_result_rows()
231        for odp, pdf in files:
232            self._add_result_row(odp, pdf)
233
234        row_h = 30
235        content_h = len(files) * (row_h + 1) + 8
236        self._scroll_area.setMaximumHeight(min(content_h, 160))
237        self._results_panel.setVisible(True)

Muestra los ficheros generados. files: list[tuple[Path, Path | None]].

def hide_open_buttons(self) -> None:
239    def hide_open_buttons(self) -> None:
240        self._results_panel.setVisible(False)
241        self._progress_bar.setValue(0)
242        self._progress_bar.setVisible(False)
def append_line(self, text: str, level: str = 'info') -> None:
244    def append_line(self, text: str, level: str = 'info') -> None:
245        color = self._colors.get(level, self._colors['info'])
246        self.text_area.appendHtml(
247            f'<span style="color:{color};white-space:pre;">{_escape(text)}</span>'
248        )
249        self.text_area.moveCursor(QTextCursor.MoveOperation.End)
def clear_log(self) -> None:
251    def clear_log(self) -> None:
252        self.text_area.clear()
def toPlainText(self) -> str:
254    def toPlainText(self) -> str:
255        return self.text_area.toPlainText()
staticMetaObject = PySide6.QtCore.QMetaObject("LogPanel" inherits "QWidget": Methods: #33 type=Signal, signature=collapse_toggled(bool), parameters=bool )