Skip to content

Commit

Permalink
Clean up Terminal implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Jun 8, 2021
1 parent 0d6ead1 commit 754dcf1
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 322 deletions.
91 changes: 44 additions & 47 deletions examples/reference/widgets/Terminal.ipynb
Expand Up @@ -10,7 +10,6 @@
"import uuid\n",
"import logging\n",
"import panel as pn\n",
"pn.config.sizing_mode = \"stretch_width\"\n",
"\n",
"pn.extension('terminal')"
]
Expand All @@ -33,13 +32,11 @@
"\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",
"- **``object``** (str): System *output* written to the Terminal.\n",
"- **``write_to_console``** (boolean): If True output is additionally written to the server console. Default value is False.\n",
"- **``options``** (dict) Initial Options for the Terminal Constructor. cf. https://xtermjs.org/docs/api/terminal/interfaces/iterminaloptions/\n",
"\n",
"\n",
"- **``clear``** (action): Clears the Terminal.\n",
"\n",
"#### Methods\n",
"\n",
Expand All @@ -54,8 +51,6 @@
"- **``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",
"\n",
"\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",
Expand All @@ -69,12 +64,10 @@
"outputs": [],
"source": [
"terminal = pn.widgets.Terminal(\n",
" object=\"Welcome to the Panel Terminal!\\nI'm based on xterm.js\\n\\n\",\n",
" height=300,\n",
" width=800,\n",
" sizing_mode=\"stretch_width\",\n",
" options={\"cursorBlink\": True},\n",
" )\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"
]
Expand All @@ -94,8 +87,8 @@
"source": [
"terminal.write(\"This is written directly to the terminal.\\n\")\n",
"terminal.write(\"Danish Characters: æøåÆØÅ\\n\")\n",
"terminal.write(\"Emoji: Python 🐍 Panel ❤️ LOL 😊 \\n\")\n",
"terminal.write(\"Links: https://awesome-panel.org\\n\")"
"terminal.write(\"Emoji: Python 🐍 Panel ❤️ 😊 \\n\")\n",
"terminal.write(\"Links: https://panel.holoviz.org\\n\")"
]
},
{
Expand Down Expand Up @@ -184,39 +177,14 @@
"metadata": {},
"outputs": [],
"source": [
"terminal.subprocess.run(args=[\"ls\", \"-l\"])"
]
},
{
"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": [
"run_bash_in_subprocess_button = pn.widgets.Button(name=\"Run Python\", button_type=\"success\")\n",
"\n",
"run_bash_in_subprocess_button.on_click(\n",
" lambda x: terminal.subprocess.run(args=\"python\")\n",
")\n",
"run_bash_in_subprocess_button"
"terminal.subprocess.run(\"ls\", \"-l\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Kill A Process\n",
"\n",
"If the process is running you can kill it"
"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:"
]
},
{
Expand All @@ -225,7 +193,7 @@
"metadata": {},
"outputs": [],
"source": [
"pn.Param(terminal.subprocess, parameters=[\"running\", \"kill\"], widgets={\"kill\": {\"button_type\": \"danger\", \"name\": \"Kill Python\"}}, show_name=False)"
"terminal"
]
},
{
Expand All @@ -248,7 +216,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Thats All Folks"
"## Run an Interactive Process in the Terminal\n",
"\n",
"You can run interactive processes like `bash`, `python`, `ipython` or similar."
]
},
{
Expand All @@ -257,7 +227,27 @@
"metadata": {},
"outputs": [],
"source": [
"terminal.write(\"Welcome to the Panel Terminal!\\nI'm based on xterm.js\\n\\n\")"
"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",
")"
]
}
],
Expand All @@ -277,7 +267,14 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.10"
"version": "3.8.8"
},
"widgets": {
"application/vnd.jupyter.widget-state+json": {
"state": {},
"version_major": 2,
"version_minor": 0
}
}
},
"nbformat": 4,
Expand Down
4 changes: 2 additions & 2 deletions panel/_templates/doc_nb_js.js
Expand Up @@ -4,12 +4,12 @@
var render_items = {{ render_items }};
root.Bokeh.embed.embed_items_notebook(docs_json, render_items);
}
if (root.Bokeh !== undefined && root.Bokeh.Panel !== undefined{% for req in requirements %} && root['{{ req }}'] !== undefined {% endfor %}{% if ipywidget %}&& (root.Bokeh.Models.registered_names().indexOf("ipywidgets_bokeh.widget.IPyWidget") > -1){% endif %}) {
if (root.Bokeh !== undefined && root.Bokeh.Panel !== undefined{% for reqs in requirements %} && ({% for req in reqs %}{% if loop.index0 > 0 %}||{% endif %} root['{{ req }}'] !== undefined{% endfor %}){% endfor %}{% if ipywidget %}&& (root.Bokeh.Models.registered_names().indexOf("ipywidgets_bokeh.widget.IPyWidget") > -1){% endif %}) {
embed_document(root);
} else {
var attempts = 0;
var timer = setInterval(function(root) {
if (root.Bokeh !== undefined && root.Bokeh.Panel !== undefined{% for req in requirements %} && root['{{ req }}'] !== undefined{% endfor %}{% if ipywidget %}&& (root.Bokeh.Models.registered_names().indexOf("ipywidgets_bokeh.widget.IPyWidget") > -1){% endif %}) {
if (root.Bokeh !== undefined && root.Bokeh.Panel !== undefined{% for reqs in requirements %} && ({% for req in reqs %}{% if loop.index0 > 0 %} || {% endif %}root['{{ req }}'] !== undefined{% endfor %}){% endfor %}{% if ipywidget %}&& (root.Bokeh.Models.registered_names().indexOf("ipywidgets_bokeh.widget.IPyWidget") > -1){% endif %}) {
clearInterval(timer);
embed_document(root);
} else if (document.readyState == "complete") {
Expand Down
16 changes: 8 additions & 8 deletions panel/config.py
Expand Up @@ -351,14 +351,14 @@ class panel_extension(_pyviz_extension):

# Check whether these are loaded before rendering
_globals = {
'deckgl': 'deck',
'echarts': 'echarts',
'katex': 'katex',
'mathjax': 'MathJax',
'plotly': 'Plotly',
'vega': 'vega',
'vtk': 'vtk',
'terminal': 'xtermjs',
'deckgl': ['deck'],
'echarts': ['echarts'],
'katex': ['katex'],
'mathjax': ['MathJax'],
'plotly': ['Plotly'],
'vega': ['vega'],
'vtk': ['vtk'],
'terminal': ['Terminal', 'xtermjs'],
}

_loaded_extensions = []
Expand Down
37 changes: 19 additions & 18 deletions panel/models/terminal.py
@@ -1,25 +1,34 @@
from collections import OrderedDict

from bokeh.core.properties import Any, Dict, Int, String
from bokeh.models import HTMLBox

from ..io.resources import bundled_files
from ..util import classproperty


XTERM_JS = "https://unpkg.com/xterm@4.11.0/lib/xterm.js"
XTERM_FIT_JS = "https://unpkg.com/xterm-addon-fit@0.5.0/lib/xterm-addon-fit.js"
XTERM_LINKS_JS = "https://unpkg.com/xterm-addon-web-links@0.4.0/lib/xterm-addon-web-links.js"


class Terminal(HTMLBox):
"""Custom Terminal Model"""

options = Dict(String, Any)
input = String()
output = String()

_clears = Int()
_value_repeats = Int()

__css_raw__ = ["https://unpkg.com/xterm@4.11.0/css/xterm.css"]

@classproperty
def __css__(cls):
return bundled_files(cls, 'css')

__javascript_raw__ = [
"https://unpkg.com/xterm@4.11.0/lib/xterm.js",
"https://unpkg.com/xterm-addon-fit@0.5.0/lib/xterm-addon-fit.js",
"https://unpkg.com/xterm-addon-web-links@0.4.0/lib/xterm-addon-web-links.js",
# "https://unpkg.com/xterm@4.11.0/lib/addons/fullscreen/fullscreen.js",
# "https://unpkg.com/xterm@4.11.0/lib/addons/search/search.js",
]
__javascript_raw__ = [XTERM_JS, XTERM_FIT_JS, XTERM_LINKS_JS]

@classproperty
def __javascript__(cls):
Expand All @@ -35,20 +44,12 @@ def __js_skip__(cls):

__js_require__ = {
'paths': OrderedDict([
("xtermjs", "https://unpkg.com/xterm@4.11.0/lib/xterm"),
("xtermjs-fit", "https://unpkg.com/xterm-addon-fit@0.5.0/lib/xterm-addon-fit"),
("xtermjs-weblinks", "https://unpkg.com/xterm-addon-web-links@0.4.0/lib/xterm-addon-web-links"),
("xtermjs", XTERM_JS[:-3]),
("xtermjs-fit", XTERM_FIT_JS[:-3]),
("xtermjs-weblinks", XTERM_LINKS_JS[:-3]),
]),
'exports': {
"xtermjs": "xtermjs",
"xtermjs-fit": "xtermjsfit",
"xtermjs-weblinks": "xtermjsweblinks",},

}

options = Dict(String, Any)
input = String()
output = String()

_clears = Int()
_value_repeats = Int()

0 comments on commit 754dcf1

Please sign in to comment.