diff options
Diffstat (limited to 'prompt_toolkit/interface.py')
-rw-r--r-- | prompt_toolkit/interface.py | 81 |
1 files changed, 70 insertions, 11 deletions
diff --git a/prompt_toolkit/interface.py b/prompt_toolkit/interface.py index 78993c37..4d803eba 100644 --- a/prompt_toolkit/interface.py +++ b/prompt_toolkit/interface.py @@ -3,6 +3,7 @@ The main `CommandLineInterface` class and logic. """ from __future__ import unicode_literals +import datetime import functools import os import signal @@ -10,8 +11,8 @@ import six import sys import textwrap import threading +import types import weakref -import datetime from subprocess import Popen @@ -461,7 +462,9 @@ class CommandLineInterface(object): """ raise NotImplementedError - def run_sub_application(self, application, done_callback=None, erase_when_done=False): + def run_sub_application(self, application, done_callback=None, erase_when_done=False, + _from_application_generator=False): + # `erase_when_done` is deprecated, set Application.erase_when_done instead. """ Run a sub :class:`~prompt_toolkit.application.Application`. @@ -477,10 +480,6 @@ class CommandLineInterface(object): only a proxy to our main event loop. The reason is that calling 'stop' --which returns the result of an application when it's done-- is handled differently. - - :param erase_when_done: Explicitely erase the sub application when - done. (This has no effect if the sub application runs in an - alternate screen.) """ assert isinstance(application, Application) assert done_callback is None or callable(done_callback) @@ -489,7 +488,8 @@ class CommandLineInterface(object): raise RuntimeError('Another sub application started already.') # Erase current application. - self.renderer.erase() + if not _from_application_generator: + self.renderer.erase() # Callback when the sub app is done. def done(): @@ -497,7 +497,7 @@ class CommandLineInterface(object): # and reset the renderer. (This reset will also quit the alternate # screen, if the sub application used that.) sub_cli._redraw() - if erase_when_done: + if erase_when_done or application.erase_when_done: sub_cli.renderer.erase() sub_cli.renderer.reset() sub_cli._is_running = False # Don't render anymore. @@ -505,8 +505,9 @@ class CommandLineInterface(object): self._sub_cli = None # Restore main application. - self.renderer.request_absolute_cursor_position() - self._redraw() + if not _from_application_generator: + self.renderer.request_absolute_cursor_position() + self._redraw() # Deliver result. if done_callback: @@ -607,11 +608,12 @@ class CommandLineInterface(object): self.renderer.reset() # Make sure to disable mouse mode, etc... else: self.renderer.erase() + self._return_value = None # Run system command. with self.input.cooked_mode(): result = func() - self._return_value = None + # Redraw interface again. self.renderer.reset() @@ -620,6 +622,63 @@ class CommandLineInterface(object): return result + def run_application_generator(self, coroutine, render_cli_done=False): + """ + EXPERIMENTAL + Like `run_in_terminal`, but takes a generator that can yield Application instances. + + Example: + + def f(): + yield Application1(...) + print('...') + yield Application2(...) + cli.run_in_terminal_async(f) + + The values which are yielded by the given coroutine are supposed to be + `Application` instances that run in the current CLI, all other code is + supposed to be CPU bound, so except for yielding the applications, + there should not be any user interaction or I/O in the given function. + """ + # Draw interface in 'done' state, or erase. + if render_cli_done: + self._return_value = True + self._redraw() + self.renderer.reset() # Make sure to disable mouse mode, etc... + else: + self.renderer.erase() + self._return_value = None + + # Loop through the generator. + g = coroutine() + assert isinstance(g, types.GeneratorType) + + def step_next(send_value=None): + " Execute next step of the coroutine." + try: + # Run until next yield, in cooked mode. + with self.input.cooked_mode(): + result = g.send(send_value) + except StopIteration: + done() + except: + done() + raise + else: + # Process yielded value from coroutine. + assert isinstance(result, Application) + self.run_sub_application(result, done_callback=step_next, + _from_application_generator=True) + + def done(): + # Redraw interface again. + self.renderer.reset() + self.renderer.request_absolute_cursor_position() + self._redraw() + + # Start processing coroutine. + step_next() + def run_system_command(self, command): """ Run system command (While hiding the prompt. When finished, all the |