# -*- coding: utf-8 -*- """ 文本输出 + 语音输出模块 """ import logging from PyQt5.QtCore import pyqtSlot from PyQt5.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QTextEdit, QLabel, QPushButton ) from uas.io.base_io import BaseOutput, IOData, IOType from uas.io.signals import io_signals logger = logging.getLogger(__name__) try: import pyttsx3 HAS_TTS = True except ImportError: HAS_TTS = False logger.warning("⚠️ pyttsx3 未安装: pip install pyttsx3") # ───────────────────────────────────────────────────────────────── # 文本输出组件 # ───────────────────────────────────────────────────────────────── class TextOutput(BaseOutput): def __init__(self): super().__init__("text_output", [IOType.TEXT, IOType.SPEECH]) def render(self, data: IOData) -> None: """将处理结果追加到文本框""" import time ts = time.strftime("%H:%M:%S", time.localtime(data.timestamp)) icon = {"text": "📝", "speech": "🎤", "file": "📁"}.get( data.io_type.value, "📦" ) result = data.processed if data.processed is not None else data.raw html = ( f'[{ts}] ' f'{icon} {data.io_type.value.upper()} ' f'{data.data_id[:8]}
' f'{result}
' f'{"─" * 50}
' ) self._output_edit.append(html) # 自动滚动到底部 sb = self._output_edit.verticalScrollBar() sb.setValue(sb.maximum()) class TextOutputWidget(QWidget, TextOutput): """ 文本输出 UI 组件 接收 output_ready 信号,将处理结果显示在滚动文本框中 """ def __init__(self, parent=None): QWidget.__init__(self, parent) TextOutput.__init__(self) self._build_ui() def _build_ui(self): layout = QVBoxLayout(self) layout.setContentsMargins(8, 8, 8, 8) layout.setSpacing(6) # 标题栏 title_row = QHBoxLayout() title = QLabel("📄 输出结果") title.setStyleSheet("font-weight: bold; font-size: 13px;") title_row.addWidget(title) title_row.addStretch() clear_btn = QPushButton("清空") clear_btn.setFixedWidth(50) clear_btn.clicked.connect(self._output_edit.clear if hasattr(self, "_output_edit") else lambda: None) title_row.addWidget(clear_btn) layout.addLayout(title_row) # 输出文本框 self._output_edit = QTextEdit() self._output_edit.setReadOnly(True) self._output_edit.setStyleSheet(""" QTextEdit { background: #1e1e1e; color: #d4d4d4; font-family: Consolas, monospace; font-size: 13px; border-radius: 6px; padding: 6px; } """) layout.addWidget(self._output_edit) # 重新绑定清空按钮 clear_btn.clicked.disconnect() clear_btn.clicked.connect(self._output_edit.clear) # 状态栏 self._status_bar = QLabel("就绪") self._status_bar.setStyleSheet("color: gray; font-size: 11px;") layout.addWidget(self._status_bar) # 连接状态信号 io_signals.status_changed.connect(self._on_status_changed) io_signals.error_occurred.connect(self._on_error) io_signals.progress_updated.connect(self._on_progress) @pyqtSlot(str, str) def _on_status_changed(self, data_id: str, status: str): self._status_bar.setText(f"状态: {status} [{data_id[:8]}]") @pyqtSlot(str, str, str) def _on_error(self, data_id: str, source: str, msg: str): self._output_edit.append( f'❌ 错误 [{data_id[:8]}] {source}: {msg}
' ) @pyqtSlot(str, int) def _on_progress(self, data_id: str, progress: int): self._status_bar.setText(f"处理中: {progress}% [{data_id[:8]}]")