gui.panels.log_panel

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

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

def hide_open_buttons(self) -> None:
224    def hide_open_buttons(self) -> None:
225        self._results_panel.setVisible(False)
226        self._progress_bar.setValue(0)
227        self._progress_bar.setVisible(False)
def append_line(self, text: str, level: str = 'info') -> None:
229    def append_line(self, text: str, level: str = 'info') -> None:
230        color = self._colors.get(level, self._colors['info'])
231        self.text_area.appendHtml(
232            f'<span style="color:{color};white-space:pre;">{_escape(text)}</span>'
233        )
234        self.text_area.moveCursor(QTextCursor.MoveOperation.End)
def clear_log(self) -> None:
236    def clear_log(self) -> None:
237        self.text_area.clear()
def toPlainText(self) -> str:
239    def toPlainText(self) -> str:
240        return self.text_area.toPlainText()
staticMetaObject = PySide6.QtCore.QMetaObject("LogPanel" inherits "QWidget": Methods: #33 type=Signal, signature=collapse_toggled(bool), parameters=bool )