Skip to content

终端模拟器 Xterm ^3.1.0

此元素是对 xterm.js 的封装,用于模拟终端。

注意:此元素仅提供前端组件,不包含底层 shell。

参数 Param说明 Description
options用于配置终端的选项字典,详见 xterm.js 文档
python
from nicegui import ui

terminal = ui.xterm({'cols': 30, 'rows': 9})
ui.timer(0, lambda: terminal.write('Hello NiceGUI!'), once=True)

ui.run()

使用 ANSI 转义码

Xterm 可以解析 ANSI 转义码来设置终端中文本的样式。使用 writeln 方法代替 write 可以在文本后自动添加换行符和回车符。

python
from nicegui import ui

terminal = ui.xterm({'cols': 30, 'rows': 9})
ui.button('添加普通文本', on_click=lambda: terminal.writeln('这是普通文本。'))
ui.button('添加蓝色文本', on_click=lambda: terminal.writeln('\x1b[34m这是蓝色文本!\x1b[0m'))
ui.button('添加粗体文本', on_click=lambda: terminal.writeln('\x1b[1m这是粗体文本!\x1b[0m'))

ui.run()

订阅事件

Xterm 在您在终端中输入或粘贴文本时会触发 "data" 事件。通常,您会将这些数据传递给 pty 或类似的后端进行处理(参见 Xterm 示例)。但您也可以将此事件连接到终端的 write 方法,以便在终端中查看数据。请注意,此演示替换了一些字符,这些字符通常由 pty 处理(换行和退格)。

您还可以处理 "bell" 事件,例如在终端的响铃被触发时播放声音(如按下 Ctrl-G)。此演示改为显示通知。

python
from nicegui import ui

terminal = ui.xterm({'cols': 30, 'rows': 9})
terminal.on_data(lambda e: terminal.write(e.data.replace('\r', '\n\r').replace('\x7f', '\x1b[0D\x1b[0K')))
terminal.on_bell(lambda: ui.notify('🔔'))

ui.run()

自动调整终端大小

您可以使用 fit 方法调整终端大小,使其行数和列数与容器的尺寸匹配。请注意,您可能还需要调整后备 pty 的大小以匹配终端的新尺寸,这可以通过订阅终端的 resize 事件来实现。另外请注意,原生 pty 模块不支持调整大小。

python
from nicegui import ui

with ui.card().classes('size-60 resize overflow-auto'):
    terminal = ui.xterm().classes('size-full')
    ui.element('q-resize-observer').on('resize', terminal.fit)

label = ui.label()
terminal.on('resize', lambda e: label.set_text(f'大小: {e.args["cols"]}x{e.args["rows"]}'))

ui.run()

显示子进程输出

您可以将子进程的输出连接到终端。请注意,subprocess.PIPE 会将输出缓冲在内存中的 StreamReader 对象中。如果您希望子进程表现得像在终端中运行一样,可能需要使用 pty。convertEol 参数会自动将换行符(\n)转换为回车符 + 换行符(\r\n),确保在显示子进程输出时正确换行。

python
import asyncio
from nicegui import ui

async def run_subprocess():
    button.disable()
    process = await asyncio.create_subprocess_exec(
        'python3', '-u', '-c',
        (
            'import time\n'
            'for i in range(5):\n'
            '    print(f"步骤 {i+1}/5: 处理中...")\n'
            '    time.sleep(0.5)\n'
            'print("\\x1b[32m✓ 所有步骤已完成!\\x1b[0m")'
        ),
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE,
    )

    async def write_to_terminal(stream: asyncio.StreamReader) -> None:
        while chunk := await stream.read(128):
            terminal.write(chunk)

    await asyncio.gather(
        write_to_terminal(process.stdout),
        write_to_terminal(process.stderr),
        process.wait(),
    )
    button.enable()

terminal = ui.xterm({'cols': 30, 'rows': 9, 'convertEol': True})
button = ui.button('运行子进程', on_click=run_subprocess)

ui.run()

更新日期: 2026 年 3 月 27 日