Python使用工具:python-prompt-toolkit库使用教程

1. 引言

Python 作为一种高级编程语言,凭借其简洁的语法和强大的功能,已成为各个领域开发者的首选工具。无论是 Web 开发中的 Django、Flask 框架,数据分析领域的 NumPy、Pandas 库,还是机器学习领域的 TensorFlow、PyTorch,Python 都展现出了卓越的适应性。在自动化测试、自然语言处理、图像处理等众多领域,Python 也有着广泛的应用。其丰富的第三方库生态系统,更是为开发者提供了极大的便利,让他们能够快速实现各种复杂的功能。

本文将介绍 Python 中一个强大的库——python-prompt-toolkit。它为命令行界面(CLI)开发提供了丰富的功能和工具,能够帮助开发者创建出更加美观、交互性更强的命令行应用程序。

2. python-prompt-toolkit 概述

python-prompt-toolkit 是一个用于构建交互式命令行界面的 Python 库。它提供了丰富的功能,如语法高亮、自动补全、历史记录、多行编辑等,使得开发者能够轻松创建出专业级的命令行工具。

2.1 用途

python-prompt-toolkit 的主要用途包括:

  • 构建交互式命令行应用程序,如 shell 工具、数据库客户端等。
  • 创建具有高级功能的终端界面,如语法高亮的编辑器、REPL(交互式解释器)等。
  • 实现自定义的命令行补全功能,提高用户输入效率。

2.2 工作原理

python-prompt-toolkit 的核心是基于事件循环的架构。它通过监听用户输入事件,并根据预设的规则进行处理,从而实现各种交互功能。例如,当用户按下 Tab 键时,库会触发自动补全逻辑;当用户输入命令时,库会对输入进行解析并执行相应的操作。

2.3 优缺点

优点:

  • 功能丰富:提供了语法高亮、自动补全、历史记录等多种功能。
  • 高度可定制:支持自定义主题、快捷键、补全规则等。
  • 跨平台兼容:可以在 Windows、Linux 和 macOS 等多种操作系统上运行。
  • 文档完善:官方文档详细,示例丰富,易于学习和使用。

缺点:

  • 学习曲线较陡:对于初学者来说,可能需要花费一定的时间来掌握其复杂的 API。
  • 性能开销:由于实现了丰富的功能,相比简单的命令行工具,可能会有一定的性能开销。

2.4 License 类型

python-prompt-toolkit 采用 BSD 许可证,这是一种较为宽松的开源许可证,允许用户自由使用、修改和分发该库。

3. python-prompt-toolkit 的安装

安装 python-prompt-toolkit 非常简单,只需要使用 pip 命令即可:

pip install prompt-toolkit

如果你使用的是 conda 环境,也可以使用以下命令安装:

conda install -c conda-forge prompt-toolkit

安装完成后,你可以通过以下命令验证是否安装成功:

python -c "import prompt_toolkit; print(prompt_toolkit.__version__)"

如果能够正常输出版本号,则说明安装成功。

4. python-prompt-toolkit 的基本使用

4.1 简单的命令行输入

下面是一个使用 python-prompt-toolkit 创建简单命令行输入的示例:

from prompt_toolkit import prompt

if __name__ == '__main__':
    user_input = prompt('请输入内容:')
    print(f'你输入的内容是:{user_input}')

这个示例展示了如何使用 prompt 函数获取用户输入。运行程序后,会显示一个提示符,等待用户输入内容。用户输入完成后,程序会将输入的内容打印出来。

4.2 带历史记录的命令行

python-prompt-toolkit 支持历史记录功能,让用户可以使用上下箭头键浏览之前的输入。以下是一个示例:

from prompt_toolkit import prompt
from prompt_toolkit.history import InMemoryHistory

if __name__ == '__main__':
    # 创建内存历史记录对象
    history = InMemoryHistory()

    while True:
        user_input = prompt('> ', history=history)

        if user_input.lower() == 'exit':
            break

        print(f'你输入的命令是:{user_input}')

在这个示例中,我们创建了一个 InMemoryHistory 对象,并将其传递给 prompt 函数。这样,用户就可以使用上下箭头键浏览之前输入的命令。当用户输入 exit 时,程序会退出循环。

4.3 自动补全功能

python-prompt-toolkit 提供了强大的自动补全功能。下面是一个简单的示例:

from prompt_toolkit import prompt
from prompt_toolkit.completion import WordCompleter

if __name__ == '__main__':
    # 创建一个单词补全器,指定可能的补全选项
    completer = WordCompleter(['python', 'java', 'c++', 'javascript', 'ruby'])

    user_input = prompt('请输入编程语言:', completer=completer)
    print(f'你选择的编程语言是:{user_input}')

在这个示例中,我们创建了一个 WordCompleter 对象,并指定了一组可能的补全选项。当用户输入时,按 Tab 键可以触发自动补全功能,显示可能的选项。

4.4 语法高亮

python-prompt-toolkit 支持语法高亮功能,可以让命令行界面更加美观和易于阅读。以下是一个示例:

from prompt_toolkit import prompt
from prompt_toolkit.lexers import PygmentsLexer
from pygments.lexers import PythonLexer

if __name__ == '__main__':
    # 使用 Pygments 词法分析器实现 Python 语法高亮
    lexer = PygmentsLexer(PythonLexer)

    user_input = prompt('请输入 Python 代码:', lexer=lexer)
    print(f'你输入的代码是:\n{user_input}')

在这个示例中,我们使用了 PygmentsLexerPythonLexer 来实现 Python 代码的语法高亮。用户输入的 Python 代码会以高亮的形式显示在命令行中。

5. 高级功能与实例

5.1 创建自定义提示符

python-prompt-toolkit 允许开发者创建自定义的提示符,使其更加个性化。以下是一个示例:

from prompt_toolkit import prompt
from prompt_toolkit.styles import Style
from prompt_toolkit.formatted_text import HTML

if __name__ == '__main__':
    # 定义样式
    style = Style.from_dict({
        'username': '#884444 bold',
        'at': '#00aa00',
        'host': '#0088ff bold',
        'path': 'ansicyan underline',
        'arrow': '#ffffff bold',
    })

    # 使用 HTML 格式定义提示符
    prompt_text = HTML('<username>user</username><at>@</at><host>localhost</host>:<path>/home/user</path><arrow>→</arrow> ')

    user_input = prompt(prompt_text, style=style)
    print(f'你输入的内容是:{user_input}')

在这个示例中,我们使用 Style 类定义了各种元素的样式,并使用 HTML 类创建了一个格式化的提示符。这样,提示符就会以指定的样式显示在命令行中。

5.2 多行输入

有时候,我们需要用户输入多行内容,比如编写一段代码或一篇文章。python-prompt-toolkit 支持多行输入功能。以下是一个示例:

from prompt_toolkit import prompt
from prompt_toolkit.validation import Validator, ValidationError

# 创建一个简单的验证器,确保输入不为空
validator = Validator.from_callable(
    lambda text: len(text.strip()) > 0,
    error_message='输入不能为空',
    move_cursor_to_end=True
)

if __name__ == '__main__':
    print('请输入多行文本(按 Ctrl+D 结束输入):')

    user_input = prompt(
        '>>> ', 
        multiline=True, 
        validator=validator,
        prompt_continuation=lambda width, line_number, is_soft_wrap: '... '
    )

    print(f'你输入的内容是:\n{user_input}')

在这个示例中,我们设置了 multiline=True 来启用多行输入模式。用户可以输入多行内容,按 Ctrl+D 结束输入。同时,我们还添加了一个验证器,确保用户输入不为空。

5.3 交互式菜单

python-prompt-toolkit 可以用于创建交互式菜单,让用户通过上下箭头键选择选项。以下是一个示例:

from prompt_toolkit.shortcuts import radiolist_dialog
from prompt_toolkit.styles import Style

if __name__ == '__main__':
    # 定义样式
    style = Style.from_dict({
        'dialog': 'bg:#88ff88',
        'dialog frame.label': 'bg:#ffffff #000000',
        'dialog.body': 'bg:#000000 #00ff00',
        'dialog shadow': 'bg:#00aa00',
    })

    # 创建单选列表对话框
    result = radiolist_dialog(
        title='选择编程语言',
        text='请选择你最喜欢的编程语言:',
        values=[
            ('python', 'Python'),
            ('java', 'Java'),
            ('c++', 'C++'),
            ('javascript', 'JavaScript'),
            ('rust', 'Rust'),
        ],
        style=style
    ).run()

    if result is not None:
        print(f'你选择的编程语言是:{result}')
    else:
        print('你取消了选择')

在这个示例中,我们使用 radiolist_dialog 函数创建了一个交互式菜单。用户可以使用上下箭头键选择选项,按 Enter 键确认选择。同时,我们还为对话框定义了自定义样式,使其更加美观。

5.4 实时输入验证

python-prompt-toolkit 支持实时输入验证,当用户输入不符合要求时,会立即显示错误信息。以下是一个示例:

from prompt_toolkit import prompt
from prompt_toolkit.validation import Validator, ValidationError
from prompt_toolkit.completion import WordCompleter

# 创建一个验证器,确保输入是一个有效的整数
class IntegerValidator(Validator):
    def validate(self, document):
        text = document.text

        if text and not text.isdigit():
            i = 0

            # 找到第一个无效字符的位置
            for i, c in enumerate(text):
                if not c.isdigit():
                    break

            raise ValidationError(
                message='请输入一个有效的整数',
                cursor_position=i
            )

if __name__ == '__main__':
    # 创建一个单词补全器,提供一些示例数字
    completer = WordCompleter(['1', '10', '100', '1000'])

    user_input = prompt(
        '请输入一个整数:', 
        validator=IntegerValidator(),
        completer=completer,
        validate_while_typing=True
    )

    print(f'你输入的整数是:{user_input}')

在这个示例中,我们创建了一个自定义的验证器 IntegerValidator,用于确保用户输入的是一个有效的整数。当用户输入不符合要求的字符时,会立即显示错误信息。同时,我们还提供了一个简单的补全器,帮助用户输入常见的数字。

6. 实际案例:创建一个简单的数据库客户端

下面我们通过一个实际案例来展示 python-prompt-toolkit 的强大功能。我们将创建一个简单的数据库客户端,支持连接 SQLite 数据库,并执行基本的 SQL 命令。

import sqlite3
from prompt_toolkit import PromptSession
from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.lexers import PygmentsLexer
from prompt_toolkit.styles import Style
from prompt_toolkit.history import FileHistory
from pygments.lexers import SqlLexer
from prompt_toolkit.validation import Validator, ValidationError

class DatabaseClient:
    def __init__(self):
        self.conn = None
        self.cursor = None
        self.db_path = None

        # 定义SQL命令补全器
        self.sql_completer = WordCompleter([
            'SELECT', 'FROM', 'WHERE', 'INSERT', 'INTO', 'VALUES',
            'UPDATE', 'SET', 'DELETE', 'CREATE', 'TABLE', 'DROP',
            'ALTER', 'INDEX', 'VIEW', 'PRAGMA', 'COMMIT', 'ROLLBACK',
            'BEGIN', 'TRANSACTION', 'NULL', 'NOT', 'DISTINCT', 'GROUP BY',
            'ORDER BY', 'LIMIT', 'OFFSET', 'HAVING', 'JOIN', 'ON', 'LEFT',
            'RIGHT', 'FULL', 'OUTER', 'INNER', 'CROSS', 'UNION', 'ALL'
        ], ignore_case=True)

        # 定义样式
        self.style = Style.from_dict({
            'prompt': 'bold #00ff00',
            'error': 'bg:#ff0000 #ffffff',
            'success': 'bg:#00aa00 #ffffff',
            'sql': '#0088ff',
        })

        # 创建历史记录文件
        self.history = FileHistory('.db_client_history')

        # 创建会话
        self.session = PromptSession(
            lexer=PygmentsLexer(SqlLexer),
            completer=self.sql_completer,
            history=self.history,
            style=self.style
        )

    def connect(self, db_path):
        """连接到SQLite数据库"""
        try:
            self.conn = sqlite3.connect(db_path)
            self.cursor = self.conn.cursor()
            self.db_path = db_path
            print(f"成功连接到数据库: {db_path}")
        except Exception as e:
            print(f"连接数据库失败: {str(e)}")

    def disconnect(self):
        """断开与数据库的连接"""
        if self.conn:
            self.conn.close()
            self.conn = None
            self.cursor = None
            self.db_path = None
            print("已断开与数据库的连接")

    def execute(self, query):
        """执行SQL查询"""
        if not self.conn:
            print("请先连接到数据库")
            return

        try:
            self.cursor.execute(query)

            # 如果是SELECT查询,显示结果
            if query.strip().upper().startswith('SELECT'):
                columns = [desc[0] for desc in self.cursor.description]
                rows = self.cursor.fetchall()

                if not rows:
                    print("查询结果为空")
                else:
                    # 打印表头
                    print(" | ".join(columns))
                    print("-" * (sum(len(str(c)) for c in columns) + len(columns) * 3 - 1))

                    # 打印数据行
                    for row in rows:
                        print(" | ".join(str(value) for value in row))

                    print(f"共查询到 {len(rows)} 条记录")
            else:
                # 对于非SELECT查询,显示受影响的行数
                print(f"操作成功,受影响的行数: {self.cursor.rowcount}")
                self.conn.commit()

        except Exception as e:
            print(f"执行SQL语句失败: {str(e)}")

    def run(self):
        """运行数据库客户端"""
        print("欢迎使用简单数据库客户端!")
        print("输入 'connect <数据库路径>' 连接到SQLite数据库")
        print("输入 'disconnect' 断开与数据库的连接")
        print("输入 'exit' 退出客户端")
        print("输入SQL语句执行数据库操作")

        while True:
            try:
                # 设置提示符
                if self.db_path:
                    prompt_text = f'[{self.db_path}]> '
                else:
                    prompt_text = '> '

                # 获取用户输入
                user_input = self.session.prompt(prompt_text).strip()

                if not user_input:
                    continue

                # 处理特殊命令
                if user_input.lower() == 'exit':
                    self.disconnect()
                    break
                elif user_input.lower().startswith('connect '):
                    db_path = user_input[8:].strip()
                    if self.db_path:
                        self.disconnect()
                    self.connect(db_path)
                elif user_input.lower() == 'disconnect':
                    self.disconnect()
                else:
                    # 执行SQL查询
                    self.execute(user_input)

            except KeyboardInterrupt:
                # 允许用户按Ctrl+C取消当前操作
                print("操作已取消")
            except EOFError:
                # 允许用户按Ctrl+D退出
                self.disconnect()
                break

if __name__ == '__main__':
    client = DatabaseClient()
    client.run()

这个数据库客户端具有以下功能:

  • 支持连接到 SQLite 数据库
  • 提供 SQL 命令的自动补全和语法高亮
  • 保存命令历史记录
  • 执行 SELECT 查询并以表格形式显示结果
  • 执行其他 SQL 命令并显示受影响的行数
  • 支持断开连接和退出客户端

使用这个客户端,你可以轻松地管理 SQLite 数据库,执行各种 SQL 操作。

7. 相关资源

  • Pypi地址:https://pypi.org/project/prompt-toolkit
  • Github地址:https://github.com/prompt-toolkit/python-prompt-toolkit
  • 官方文档地址:https://python-prompt-toolkit.readthedocs.io/en/master/

关注我,每天分享一个实用的Python自动化工具。