base_agent/uas/inout/inputs/text_input.py

156 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
文本输入模块
支持:
- 控制台标准输入(后台线程读取,不阻塞 Qt 主循环)
- 程序内直接注入inject
- PyQt5 UI 文本框输入
"""
import logging
import sys
from PyQt5.QtCore import QThread, pyqtSlot, Qt
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout,
QLineEdit, QTextEdit, QPushButton, QLabel
)
from uas.io.base_io import BaseInput, IOType
logger = logging.getLogger(__name__)
# ─────────────────────────────────────────────────────────────────
# 控制台输入(后台 QThread 读取 stdin
# ─────────────────────────────────────────────────────────────────
class _StdinReaderThread(QThread):
def __init__(self, callback, parent=None):
super().__init__(parent)
self._callback = callback
self._running = False
def run(self):
self._running = True
logger.info("stdin 读取线程启动")
while self._running:
try:
line = sys.stdin.readline()
if not line:
break
text = line.rstrip("\n")
if text:
self._callback(text)
except Exception as e:
logger.error(f"stdin 读取异常: {e}")
break
logger.info("stdin 读取线程退出")
def stop(self):
self._running = False
class ConsoleTextInput(BaseInput):
"""
控制台文本输入
在独立 QThread 中读取 stdin通过信号槽安全回传主线程
"""
def __init__(self, parent=None):
super().__init__("console_stdin", IOType.TEXT, parent)
self._thread = _StdinReaderThread(self._on_line_read, self)
def start(self) -> None:
self._running = True
self._thread.start()
logger.info("✅ ConsoleTextInput 已启动")
def stop(self) -> None:
self._running = False
self._thread.stop()
self._thread.wait(2000)
logger.info("🛑 ConsoleTextInput 已停止")
def _on_line_read(self, text: str):
self._emit_input(text, metadata={"source": "stdin"})
def inject(self, text: str) -> None:
"""程序内直接注入文本(用于测试或 API 调用)"""
self._emit_input(text, metadata={"source": "inject"})
# ─────────────────────────────────────────────────────────────────
# PyQt5 UI 文本输入组件
# ─────────────────────────────────────────────────────────────────
class TextInputWidget(QWidget, BaseInput):
"""
带 UI 界面的文本输入组件
包含:单行输入框 + 多行文本框 + 发送按钮
"""
def __init__(self, parent=None):
QWidget.__init__(self, parent)
BaseInput.__init__(self, "ui_text_input", IOType.TEXT)
self._build_ui()
def _build_ui(self):
layout = QVBoxLayout(self)
layout.setContentsMargins(8, 8, 8, 8)
layout.setSpacing(6)
# 标题
title = QLabel("📝 文本输入")
title.setStyleSheet("font-weight: bold; font-size: 13px;")
layout.addWidget(title)
# 多行输入区
self._text_edit = QTextEdit()
self._text_edit.setPlaceholderText("在此输入多行文本...")
self._text_edit.setMinimumHeight(80)
layout.addWidget(self._text_edit)
# 单行 + 发送
row = QHBoxLayout()
self._line_edit = QLineEdit()
self._line_edit.setPlaceholderText("快速输入(回车发送)...")
self._line_edit.returnPressed.connect(self._on_send_line)
row.addWidget(self._line_edit)
self._send_btn = QPushButton("发送")
self._send_btn.setFixedWidth(60)
self._send_btn.clicked.connect(self._on_send_line)
row.addWidget(self._send_btn)
self._send_multi_btn = QPushButton("发送全文")
self._send_multi_btn.setFixedWidth(70)
self._send_multi_btn.clicked.connect(self._on_send_multi)
row.addWidget(self._send_multi_btn)
layout.addLayout(row)
# 清空按钮
clear_btn = QPushButton("清空")
clear_btn.setFixedWidth(60)
clear_btn.clicked.connect(self._text_edit.clear)
layout.addWidget(clear_btn, alignment=Qt.AlignRight)
@pyqtSlot()
def _on_send_line(self):
text = self._line_edit.text().strip()
if text:
self._emit_input(text, metadata={"source": "ui_line"})
self._line_edit.clear()
@pyqtSlot()
def _on_send_multi(self):
text = self._text_edit.toPlainText().strip()
if text:
self._emit_input(text, metadata={"source": "ui_multiline"})
def start(self) -> None:
self._running = True
self.show()
def stop(self) -> None:
self._running = False