{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import sys\n", "import uuid\n", "import logging\n", "import panel as pn\n", "\n", "pn.extension('terminal')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The ``Terminal`` provides a way to display outputs or logs from running processes as well as an interactive terminal based on for example Bash, Python or IPython. The Terminal is based on [Xterm.js](https://xtermjs.org/) which enables\n", "\n", "- Terminal apps that just work: Xterm.js works with most terminal apps such as bash, vim and tmux, this includes support for curses-based apps and mouse event support\n", "- Performance: Xterm.js is really fast, it even includes a GPU-accelerated renderer\n", "- Rich unicode support: Supports CJK, emojis and IMEs\n", "\n", "[![Xterm.js](https://raw.githubusercontent.com/xtermjs/xterm.js/master/logo-full.png)](https://xtermjs.org/)\n", "\n", "### Terminal Widget\n", "\n", "#### Parameters:\n", "\n", "For layout and styling related parameters see the [customization user guide](../../user_guide/Customization.ipynb).\n", "\n", "- **``clear``** (action): Clears the Terminal.\n", "- **``options``** (dict) Initial Options for the Terminal Constructor. cf. https://xtermjs.org/docs/api/terminal/interfaces/iterminaloptions/\n", "- **``output``** (str): System *output* written to the Terminal.\n", "- **``value``** (str): User *input* received from the Terminal.\n", "- **``write_to_console``** (boolean): If True output is additionally written to the server console. Default value is False.\n", "\n", "#### Methods\n", "\n", "* **``write``**: Writes the specified string object to the Terminal.\n", "\n", "### Terminal Subprocess\n", "\n", "The `Terminal.subprocess` property makes it easy for you to run subprocesses like `ls`, `ls -l`, `bash`, `python` and `ipython` in the terminal. \n", "\n", "#### Parameters\n", "\n", "- **``args``** (str, list): The arguments used to run the subprocess. This may be a string or a list. The string cannot contain spaces. See [subprocess.run](https://docs.python.org/3/library/subprocess.html) for more details.\n", "- **``kwargs``** (dict): Any other arguments to run the subprocess. See [subprocess.run](https://docs.python.org/3/library/subprocess.html) for more details.\n", "- **``running``** (boolean, readonly): Whether or not the subprocess is running. Defaults to False.\n", "- **``run``** (action): Executes `subprocess.run` in a child process using the args and kwargs parameters provided as arguments or as parameter values on the instance. The child process is running in a *pseudo terminal* ([pty](https://docs.python.org/3/library/pty.html)) which is then connected to the Terminal.\n", "- **``kill``** (action): Kills the subprocess if it is running.\n", "\n", "___" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "terminal = pn.widgets.Terminal(\n", " \"Welcome to the Panel Terminal!\\nI'm based on xterm.js\\n\\n\",\n", " options={\"cursorBlink\": True},\n", " height=300, sizing_mode='stretch_width'\n", ")\n", "\n", "terminal" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Writing strings to the terminal" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "terminal.write(\"This is written directly to the terminal.\\n\")\n", "terminal.write(\"Danish Characters: æøåÆØÅ\\n\")\n", "terminal.write(\"Emoji: Python 🐍 Panel ❤️ 😊 \\n\")\n", "terminal.write(\"Links: https://panel.holoviz.org\\n\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Writing stdout to the terminal" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sys.stdout = terminal\n", "print(\"This print statement is redirected from stdout to the Panel Terminal\")\n", "\n", "sys.stdout = sys.__stdout__\n", "print(\"This print statement is again redirected to the server console\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Logging to the terminal" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "logger = logging.getLogger(\"terminal\")\n", "logger.setLevel(logging.DEBUG)\n", "\n", "stream_handler = logging.StreamHandler(terminal) # NOTE THIS\n", "stream_handler.terminator = \" \\n\"\n", "formatter = logging.Formatter(\"%(asctime)s [%(levelname)s]: %(message)s\")\n", "\n", "stream_handler.setFormatter(formatter)\n", "stream_handler.setLevel(logging.DEBUG)\n", "logger.addHandler(stream_handler)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "logger.info(\"Hello Info Logger\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Streaming to the terminal\n", "\n", "We only do this to a reduced amount as you can reach a general rate limit in the notebook." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for i in range(0, 50):\n", " logger.info(uuid.uuid4()) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Run SubProcess and direct output to Terminal" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "terminal.subprocess.run(\"ls\", \"-l\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let us review the output so far since a static rendering of this page will not dynamically update the contents of the terminal displayed above:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "terminal" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Clear the Terminal" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "terminal.clear()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Run an Interactive Process in the Terminal\n", "\n", "You can run interactive processes like `bash`, `python`, `ipython` or similar." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "subprocess_terminal = pn.widgets.Terminal(\n", " options={\"cursorBlink\": True},\n", " height=300, sizing_mode='stretch_width'\n", ")\n", "\n", "run_python = pn.widgets.Button(name=\"Run Python\", button_type=\"success\")\n", "run_python.on_click(\n", " lambda x: subprocess_terminal.subprocess.run(\"python\")\n", ")\n", "\n", "kill = pn.widgets.Button(name=\"Kill Python\", button_type=\"danger\")\n", "kill.on_click(\n", " lambda x: subprocess_terminal.subprocess.kill()\n", ")\n", "\n", "pn.Column(\n", " pn.Row(run_python, kill, subprocess_terminal.subprocess.param.running),\n", " subprocess_terminal,\n", " sizing_mode='stretch_both',\n", " height=500\n", ")" ] } ], "metadata": { "language_info": { "name": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 4 }