def __init__( self, python_input: "PythonInput", lexer=PythonLexer, extra_body=None, extra_toolbars=None, extra_buffer_processors=None, input_buffer_height: Optional[AnyDimension] = None, ) -> None: D = Dimension extra_body = [extra_body] if extra_body else [] extra_toolbars = extra_toolbars or [] extra_buffer_processors = extra_buffer_processors or [] input_buffer_height = input_buffer_height or D(min=6) search_toolbar = SearchToolbar(python_input.search_buffer) def create_python_input_window(): def menu_position(): """ When there is no autocompletion menu to be shown, and we have a signature, set the pop-up position at `bracket_start`. """ b = python_input.default_buffer if b.complete_state is None and python_input.signatures: row, col = python_input.signatures[0].bracket_start index = b.document.translate_row_col_to_index(row - 1, col) return index return Window( BufferControl( buffer=python_input.default_buffer, search_buffer_control=search_toolbar.control, lexer=lexer, include_default_input_processors=False, input_processors=[ ConditionalProcessor( processor=HighlightIncrementalSearchProcessor(), filter=has_focus(SEARCH_BUFFER) | has_focus(search_toolbar.control), ), HighlightSelectionProcessor(), DisplayMultipleCursors(), TabsProcessor(), # Show matching parentheses, but only while editing. ConditionalProcessor( processor=HighlightMatchingBracketProcessor( chars="[](){}"), filter=has_focus(DEFAULT_BUFFER) & ~is_done & Condition(lambda: python_input. highlight_matching_parenthesis), ), ConditionalProcessor(processor=AppendAutoSuggestion(), filter=~is_done), ] + extra_buffer_processors, menu_position=menu_position, # Make sure that we always see the result of an reverse-i-search: preview_search=True, ), left_margins=[PythonPromptMargin(python_input)], # Scroll offsets. The 1 at the bottom is important to make sure # the cursor is never below the "Press [Meta+Enter]" message # which is a float. scroll_offsets=ScrollOffsets(bottom=1, left=4, right=4), # As long as we're editing, prefer a minimal height of 6. height=(lambda: (None if get_app().is_done or python_input. show_exit_confirmation else input_buffer_height)), wrap_lines=Condition(lambda: python_input.wrap_lines), ) sidebar = python_sidebar(python_input) self.exit_confirmation = create_exit_confirmation(python_input) root_container = HSplit([ VSplit([ HSplit([ FloatContainer( content=HSplit([create_python_input_window()] + extra_body), floats=[ Float( xcursor=True, ycursor=True, content=ConditionalContainer( content=CompletionsMenu( scroll_offset=( lambda: python_input. completion_menu_scroll_offset), max_height=12, ), filter=show_completions_menu(python_input), ), ), Float( xcursor=True, ycursor=True, content=ConditionalContainer( content=MultiColumnCompletionsMenu(), filter=show_multi_column_completions_menu( python_input), ), ), Float( xcursor=True, ycursor=True, content=signature_toolbar(python_input), ), Float( left=2, bottom=1, content=self.exit_confirmation, ), Float( bottom=0, right=0, height=1, content=meta_enter_message(python_input), hide_when_covering_content=True, ), Float( bottom=1, left=1, right=0, content=python_sidebar_help(python_input), ), ], ), ArgToolbar(), search_toolbar, SystemToolbar(), ValidationToolbar(), ConditionalContainer( content=CompletionsToolbar(), filter=show_completions_toolbar(python_input) & ~is_done, ), # Docstring region. ConditionalContainer( content=Window( height=D.exact(1), char="\u2500", style="class:separator", ), filter=HasSignature(python_input) & ShowDocstring(python_input) & ~is_done, ), ConditionalContainer( content=Window( BufferControl( buffer=python_input.docstring_buffer, lexer=SimpleLexer(style="class:docstring"), # lexer=PythonLexer, ), height=D(max=12), ), filter=HasSignature(python_input) & ShowDocstring(python_input) & ~is_done, ), ]), ConditionalContainer( content=HSplit([ sidebar, Window(style="class:sidebar,separator", height=1), python_sidebar_navigation(python_input), ]), filter=ShowSidebar(python_input) & ~is_done, ), ]) ] + extra_toolbars + [ VSplit([ status_bar(python_input), show_sidebar_button_info(python_input) ]) ]) self.layout = Layout(root_container) self.sidebar = sidebar
from prompt_toolkit import Application, HTML from prompt_toolkit.layout.containers import Window from prompt_toolkit.layout.controls import FormattedTextControl from prompt_toolkit.layout.layout import Layout from prompt_toolkit.key_binding import KeyBindings # naformátovaná zpráva message = HTML("<ansired>Hello</ansired> <ansiblue>world!</ansiblue>") # ovládací prvek s naformátovaným textem text = FormattedTextControl(text=message) # okno obsahující jediný ovládací prvek window = Window(content=text) # správce rozvržení layout = Layout(window) # napojení na klávesové zkratky key_bindings = KeyBindings() @key_bindings.add('escape') def on_escape_press(event): """Callback funkce volaná při stisku klávesy Esc.""" print("\n\n[escape]\n\n") event.app.exit()
"spider", "turkey", "turtle", ], ignore_case=True, ) # The layout buff = Buffer(completer=animal_completer, complete_while_typing=True) body = FloatContainer( content=HSplit( [ Window( FormattedTextControl('Press "q" to quit.'), height=1, style="reverse" ), Window(BufferControl(buffer=buff)), ] ), floats=[ Float( xcursor=True, ycursor=True, content=CompletionsMenu(max_height=16, scroll_offset=1), ) ], ) # Key bindings
def signature_toolbar(python_input): """ Return the `Layout` for the signature. """ def get_tokens(cli): result = [] append = result.append Signature = Token.Toolbar.Signature if python_input.signatures: sig = python_input.signatures[0] # Always take the first one. append((Signature, ' ')) try: append((Signature, sig.full_name)) except IndexError: # Workaround for #37: https://github.com/jonathanslenders/python-prompt-toolkit/issues/37 # See also: https://github.com/davidhalter/jedi/issues/490 return [] append((Signature.Operator, '(')) try: enumerated_params = enumerate(sig.params) except AttributeError: # Workaround for #136: https://github.com/jonathanslenders/ptpython/issues/136 # AttributeError: 'Lambda' object has no attribute 'get_subscope_by_name' return [] for i, p in enumerated_params: # Workaround for #47: 'p' is None when we hit the '*' in the signature. # and sig has no 'index' attribute. # See: https://github.com/jonathanslenders/ptpython/issues/47 # https://github.com/davidhalter/jedi/issues/598 description = (p.description if p else '*') #or '*' sig_index = getattr(sig, 'index', 0) if i == sig_index: # Note: we use `_Param.description` instead of # `_Param.name`, that way we also get the '*' before args. append((Signature.CurrentName, str(description))) else: append((Signature, str(description))) append((Signature.Operator, ', ')) if sig.params: # Pop last comma result.pop() append((Signature.Operator, ')')) append((Signature, ' ')) return result return ConditionalContainer( content=Window(TokenListControl(get_tokens), height=LayoutDimension.exact(1)), filter= # Show only when there is a signature HasSignature(python_input) & # And there are no completions to be shown. (would cover signature pop-up.) ~(HasCompletions() & (show_completions_menu(python_input) | show_multi_column_completions_menu(python_input))) # Signature needs to be shown. & ShowSignature(python_input) & # Not done yet. ~IsDone())
def python_sidebar(python_input): """ Create the `Layout` for the sidebar with the configurable options. """ def get_tokens(cli): tokens = [] T = Token.Sidebar def append_category(category): tokens.extend([ (T, ' '), (T.Title, ' %-36s' % category.title), (T, '\n'), ]) def append(index, label, status): selected = index == python_input.selected_option_index @if_mousedown def select_item(cli, mouse_event): python_input.selected_option_index = index @if_mousedown def goto_next(cli, mouse_event): " Select item and go to next value. " python_input.selected_option_index = index option = python_input.selected_option option.activate_next() token = T.Selected if selected else T tokens.append((T, ' >' if selected else ' ')) tokens.append((token.Label, '%-24s' % label, select_item)) tokens.append((token.Status, ' ', select_item)) tokens.append((token.Status, '%s' % status, goto_next)) if selected: tokens.append((Token.SetCursorPosition, '')) tokens.append((token.Status, ' ' * (13 - len(status)), goto_next)) tokens.append((T, '<' if selected else '')) tokens.append((T, '\n')) i = 0 for category in python_input.options: append_category(category) for option in category.options: append(i, option.title, '%s' % option.get_current_value()) i += 1 tokens.pop() # Remove last newline. return tokens class Control(TokenListControl): def move_cursor_down(self, cli): python_input.selected_option_index += 1 def move_cursor_up(self, cli): python_input.selected_option_index -= 1 return ConditionalContainer(content=Window( Control(get_tokens, Char(token=Token.Sidebar), has_focus=ShowSidebar(python_input) & ~IsDone()), width=LayoutDimension.exact(43), height=LayoutDimension(min=3), scroll_offsets=ScrollOffsets(top=1, bottom=1)), filter=ShowSidebar(python_input) & ~IsDone())
def question(message, **kwargs): # TODO add bottom-bar (Move up and down to reveal more choices) # TODO extract common parts for list, checkbox, rawlist, expand # TODO validate if not 'choices' in kwargs: raise PromptParameterException('choices') # this does not implement default, use checked... if 'default' in kwargs: raise ValueError('Checkbox does not implement \'default\' ' 'use \'checked\':True\' in choice!') choices = kwargs.pop('choices', None) validator = setup_simple_validator(kwargs) # TODO style defaults on detail level style = kwargs.pop('style', default_style) pointer_index = kwargs.pop('pointer_index', 0) ic = InquirerControl(choices, pointer_index) qmark = kwargs.pop('qmark', '?') def get_prompt_tokens(cli): tokens = [] tokens.append((Token.QuestionMark, qmark)) tokens.append((Token.Question, ' %s ' % message)) if ic.answered: nbr_selected = len(ic.selected_options) if nbr_selected == 0: tokens.append((Token.Answer, ' done')) elif nbr_selected == 1: tokens.append((Token.Answer, ' [%s]' % ic.selected_options[0])) else: tokens.append( (Token.Answer, ' done (%d selections)' % nbr_selected)) else: tokens.append((Token.Instruction, ' (<up>, <down> to move, <space> to select, <a> ' 'to toggle, <i> to invert)')) return tokens # assemble layout layout = HSplit([ Window(height=D.exact(1), content=TokenListControl(get_prompt_tokens, align_center=False)), ConditionalContainer(Window(ic, width=D.exact(43), height=D(min=3), scroll_offsets=ScrollOffsets(top=1, bottom=1)), filter=~IsDone()) ]) # key bindings manager = KeyBindingManager.for_prompt() @manager.registry.add_binding(Keys.ControlQ, eager=True) @manager.registry.add_binding(Keys.ControlC, eager=True) def _(event): raise KeyboardInterrupt() # event.cli.set_return_value(None) @manager.registry.add_binding(' ', eager=True) def toggle(event): pointed_choice = ic.choices[ic.pointer_index][1] # value if pointed_choice in ic.selected_options: ic.selected_options.remove(pointed_choice) else: ic.selected_options.append(pointed_choice) @manager.registry.add_binding('i', eager=True) def invert(event): inverted_selection = [ c[1] for c in ic.choices if not isinstance(c, Separator) and c[1] not in ic.selected_options and not c[2] ] ic.selected_options = inverted_selection @manager.registry.add_binding('a', eager=True) def all(event): all_selected = True # all choices have been selected for c in ic.choices: if not isinstance(c, Separator) and c[ 1] not in ic.selected_options and not c[2]: # add missing ones ic.selected_options.append(c[1]) all_selected = False if all_selected: ic.selected_options = [] @manager.registry.add_binding(Keys.Down, eager=True) def move_cursor_down(event): def _next(): ic.pointer_index = ((ic.pointer_index + 1) % ic.line_count) _next() while isinstance(ic.choices[ic.pointer_index], Separator) or \ ic.choices[ic.pointer_index][2]: _next() @manager.registry.add_binding(Keys.Up, eager=True) def move_cursor_up(event): def _prev(): ic.pointer_index = ((ic.pointer_index - 1) % ic.line_count) _prev() while isinstance(ic.choices[ic.pointer_index], Separator) or \ ic.choices[ic.pointer_index][2]: _prev() @manager.registry.add_binding(Keys.Enter, eager=True) def set_answer(event): ic.answered = True # TODO use validator event.cli.set_return_value(ic.get_selected_values()) return Application(layout=layout, key_bindings_registry=manager.registry, mouse_support=True, style=style)
def __init__( self, message: InquirerPyMessage, choices: InquirerPyListChoices, default: InquirerPyDefault = None, style: InquirerPyStyle = None, vi_mode: bool = False, qmark: str = "?", amark: str = "?", pointer: str = INQUIRERPY_POINTER_SEQUENCE, instruction: str = "", long_instruction: str = "", transformer: Callable[[Any], Any] = None, filter: Callable[[Any], Any] = None, height: Union[int, str] = None, max_height: Union[int, str] = None, multiselect: bool = False, marker: str = INQUIRERPY_POINTER_SEQUENCE, marker_pl: str = " ", border: bool = False, validate: InquirerPyValidate = None, invalid_message: str = "Invalid input", keybindings: Dict[str, List[Dict[str, Any]]] = None, show_cursor: bool = True, cycle: bool = True, wrap_lines: bool = True, raise_keyboard_interrupt: bool = True, mandatory: bool = True, mandatory_message: str = "Mandatory prompt", session_result: InquirerPySessionResult = None, ) -> None: if not hasattr(self, "_content_control"): self.content_control = InquirerPyListControl( choices=choices, default=default, pointer=pointer, marker=marker, session_result=session_result, multiselect=multiselect, marker_pl=marker_pl, ) super().__init__( message=message, style=style, border=border, vi_mode=vi_mode, qmark=qmark, amark=amark, instruction=instruction, long_instruction=long_instruction, transformer=transformer, filter=filter, validate=validate, invalid_message=invalid_message, multiselect=multiselect, keybindings=keybindings, cycle=cycle, wrap_lines=wrap_lines, raise_keyboard_interrupt=raise_keyboard_interrupt, mandatory=mandatory, mandatory_message=mandatory_message, session_result=session_result, ) self._show_cursor = show_cursor self._dimmension_height, self._dimmension_max_height = calculate_height( height, max_height, height_offset=self.height_offset) main_content_window = Window( content=self.content_control, height=Dimension( max=self._dimmension_max_height, preferred=self._dimmension_height, ), dont_extend_height=True, ) if self._border: main_content_window = Frame(main_content_window) self._layout = FloatContainer( content=HSplit([ MessageWindow( message=self._get_prompt_message_with_cursor if self._show_cursor else self._get_prompt_message, filter=True, wrap_lines=self._wrap_lines, show_cursor=self._show_cursor, ), ConditionalContainer(main_content_window, filter=~IsDone()), ConditionalContainer( Window(content=DummyControl()), filter=~IsDone() & self._is_displaying_long_instruction, ), InstructionWindow( message=self._long_instruction, filter=~IsDone() & self._is_displaying_long_instruction, wrap_lines=self._wrap_lines, ), ]), floats=[ ValidationFloat( invalid_message=self._get_error_message, filter=self._is_invalid & ~IsDone(), wrap_lines=self._wrap_lines, left=0, bottom=self._validation_window_bottom_offset, ), ], ) self.application = Application( layout=Layout(self._layout), style=self._style, key_bindings=self._kb, after_render=self._after_render, )
def __init__(self, command=['/bin/bash'], before_exec_func=None, bell_func=None, style='', width=None, height=None, done_callback=None): self.terminal_control = _TerminalControl( command=command, before_exec_func=before_exec_func, bell_func=bell_func, done_callback=done_callback) self.terminal_window = _Window(terminal_control=self.terminal_control, content=self.terminal_control, wrap_lines=False) # Key bindigns for copy buffer. kb = KeyBindings() @kb.add('c-c') def _(event): self.exit_copy_mode() @kb.add('space') def _(event): " Reset selection. " event.current_buffer.start_selection() @kb.add('enter', filter=has_selection) def _(event): " Reset selection. " data = event.current_buffer.copy_selection() event.app.clipboard.set_data(data) self.search_toolbar = SearchToolbar( forward_search_prompt='Search down: ', backward_search_prompt='Search up: ') self.copy_buffer = Buffer(read_only=True) self.copy_buffer_control = BufferControl( buffer=self.copy_buffer, search_buffer_control=self.search_toolbar.control, include_default_input_processors=False, input_processors=[ _UseStyledTextProcessor(self), HighlightSelectionProcessor(), HighlightSearchProcessor(), HighlightIncrementalSearchProcessor(), ], preview_search= True, # XXX: not sure why we need twice preview_search. key_bindings=kb) self.copy_window = Window(content=self.copy_buffer_control, wrap_lines=False) self.is_copying = False self.styled_copy_lines = [ ] # List of lists of (style, text) tuples, for each line. @Condition def is_copying(): return self.is_copying self.container = FloatContainer( content=HSplit( [ # Either show terminal window or copy buffer. VSplit([ # XXX: this nested VSplit should not have been necessary, # but the ConditionalContainer which width can become # zero will collapse the other elements. ConditionalContainer(self.terminal_window, filter=~is_copying), ConditionalContainer(self.copy_window, filter=is_copying), ]), ConditionalContainer(self.search_toolbar, filter=is_copying), ], style=style, width=width, height=height), floats=[ Float(top=0, right=0, height=1, content=ConditionalContainer(Window( content=FormattedTextControl( text=self._copy_position_formatted_text), style='class:copy-mode-cursor-position'), filter=is_copying)) ])
SPACE = QLabel(" ") preview = FormattedTextControl() preview_frame = PreviewFrame( preview, title="Preview", style="fg:#AAAAAA bold", ) root_container = MenuContainer( body=HSplit([ VSplit([ HSplit([ open_file_frame, search_toolbar, ]), Window(preview), ], ), VSplit( [ QLabel("<F1=Help>"), SPACE, QLabel("<F5=Run>"), SPACE, QLabel("<CTRL+R=Run>"), ], style="bg:#00AAAA fg:white bold", height=1, ), ]), menu_items=[ MenuItem(
def play(self, vlc_params: str = DEFAULT_VLC_PARAMS) -> None: if not self.musics: self.warn('Empty playlist') return try: if platform.system() == 'Windows': vlc_path = r'C:\Program Files\VideoLAN\VLC' logger.debug(f"Adding DLL folder {vlc_path}") os.add_dll_directory(vlc_path) # type: ignore import vlc # type: ignore instance = vlc.Instance(vlc_params) devices = instance.audio_output_enumerate_devices() if not devices: logger.warning('maybe no audio output detected...') if not instance: logger.critical('Unable to start VLC instance') return player = instance.media_list_player_new() if not player: logger.critical('Unable to create VLC player') return media_list = instance.media_list_new(list(self.links)) if not media_list: logger.critical('Unable to create VLC media list') return player.set_media_list(media_list) bindings = KeyBindings() def print_help() -> None: print_formatted_text( HTML( '<violet>Bindings: q = quit | p = play | s = pause/continue | right = next song | left = previous song | l = playlist</violet>' )) @bindings.add('p') def _play_binding(event: Any) -> None: # pylint: disable=unused-argument def play() -> None: """Play song""" player.play() _ = run_in_terminal(play) @bindings.add('q') def _quit_binding(event: Any) -> None: player.pause() event.app.exit() @bindings.add('s') def _pause_binding(event: Any) -> None: # pylint: disable=unused-argument player.pause() @bindings.add('l') def _playlist_binding(event: Any) -> None: # pylint: disable=unused-argument def playlist() -> None: """List songs""" media_player = player.get_media_player() media = media_player.get_media() media.parse() current_artist = media.get_meta(vlc.Meta.Artist) current_album = media.get_meta(vlc.Meta.Album) current_title = media.get_meta(vlc.Meta.Title) self.print(output='table', current_artist=current_artist, current_album=current_album, current_title=current_title) _ = run_in_terminal(playlist) @bindings.add('right') def _next_binding(event: Any) -> None: # pylint: disable=unused-argument player.next() @bindings.add('left') def _previous_binding(event: Any) -> None: # pylint: disable=unused-argument player.previous() @bindings.add('h') def _help(event: Any) -> None: # pylint: disable=unused-argument def _print_help() -> None: print_help() _ = run_in_terminal(_print_help) get_app().invalidate() def bottom_toolbar() -> Any: media_player = player.get_media_player() media = media_player.get_media() media.parse() media_time = formatted_seconds_to_human( round(media_player.get_time() / 1000)) media_length = formatted_seconds_to_human( round(media_player.get_length() / 1000)) artist = media.get_meta(vlc.Meta.Artist) album = media.get_meta(vlc.Meta.Album) title = media.get_meta(vlc.Meta.Title) current = f'({media_time} / {media_length}) {artist} - {album} - {title}' get_app().invalidate() return HTML(f'Current song: {current}') print_help() player.play() root_container = HSplit([ Window(FormattedTextControl(lambda: bottom_toolbar, style='class:bottom-toolbar.text'), style='class:bottom-toolbar') ]) layout = Layout(root_container) app: Any = Application(layout=layout, key_bindings=bindings) app.run() except Exception as e: # pylint:disable=broad-except logger.exception(e)
class JobWidgetBase(abc.ABC): """User-interaction and information display for :class:`~.jobs.JobBase` instance""" _no_runtime_widget = Window( dont_extend_height=True, style='class:info', ) def __init__(self, job, app): self._job = job self._app = app self.setup() self.job.signal.register('info', lambda _: self.invalidate()) self.job.signal.register('warning', lambda _: self.invalidate()) main_widget = HSplit( children=[ # Status information or user interaction ConditionalContainer( filter=Condition(lambda: not self.job.is_finished), content=self.runtime_widget or self._no_runtime_widget, ), # Result ConditionalContainer( filter=Condition(lambda: self.job.output), content=self.output_widget, ), # Additional info that isn't part of the job's main result # (e.g. CreateTorrentJobWidget can show the files in the # torrent, but the output is the torrent file path.) ConditionalContainer( filter=Condition(lambda: not self.job.is_finished and bool(self.job.info)), content=self.info_widget, ), # Warnings ConditionalContainer( filter=Condition(lambda: bool(self.job.warnings)), content=self.warnings_widget, ), # Errors ConditionalContainer( filter=Condition(lambda: bool(self.job.errors)), content=self.errors_widget, ), ], ) label = widgets.HLabel( group='jobs', text=self.job.label, style='class:label', content=main_widget, ) self._container = ConditionalContainer( filter=Condition(lambda: self.job.errors or not self.job.hidden), content=label, ) @property def job(self): """Underlying :class:`~.JobBase` instance""" return self._job @abc.abstractmethod def setup(self): """ Called on object creation Create widgets and register :attr:`job` callbacks. """ @property @abc.abstractmethod def runtime_widget(self): """ Interactive or status that is displayed while this job is running :return: :class:`~.prompt_toolkit.layout.containers.Window` object or `None` """ @property def output_widget(self): """ Job :attr:`~.JobBase.output` :return: :class:`~.prompt_toolkit.layout.containers.Window` object """ return Window( style='class:output', # FIXME: If output is empty, prompt-toolkit ignores the # "dont_extend_height" argument. Using a space (0x20) as a # placeholder seems to prevent this issue. content=FormattedTextControl(lambda: '\n'.join(self.job.output) or ' '), dont_extend_height=True, wrap_lines=True, ) @property def info_widget(self): """ :attr:`~.JobBase.info` that is only displayed while this job is running :return: :class:`~.prompt_toolkit.layout.containers.Window` object """ return Window( style='class:info', content=FormattedTextControl(lambda: str(self.job.info)), dont_extend_height=True, wrap_lines=True, ) @property def warnings_widget(self): """ Any :attr:`~.JobBase.warnings` :return: :class:`~.prompt_toolkit.layout.containers.Window` object """ return Window( style='class:warning', content=FormattedTextControl(lambda: '\n'.join(str(e) for e in self.job.warnings)), dont_extend_height=True, wrap_lines=True, ) @property def errors_widget(self): """ Any :attr:`~.JobBase.errors` :return: :class:`~.prompt_toolkit.layout.containers.Window` object """ return Window( style='class:error', content=FormattedTextControl(lambda: '\n'.join(str(e) for e in self.job.errors)), dont_extend_height=True, wrap_lines=True, ) @property def is_interactive(self): """Whether this job needs user interaction""" if self.runtime_widget: for c in walk(to_container(self.runtime_widget), skip_hidden=True): if isinstance(c, Window) and c.content.is_focusable(): return True return False def invalidate(self): """Schedule redrawing of the TUI""" self._app.invalidate() def __pt_container__(self): return self._container
def __init__(self, history): search_toolbar = SearchToolbar() self.help_buffer_control = BufferControl( buffer=history.help_buffer, lexer=PygmentsLexer(RstLexer)) help_window = _create_popup_window( title='History Help', body=Window( content=self.help_buffer_control, right_margins=[ScrollbarMargin(display_arrows=True)], scroll_offsets=ScrollOffsets(top=2, bottom=2))) self.default_buffer_control = BufferControl( buffer=history.default_buffer, input_processors=[GrayExistingText(history.history_mapping)], lexer=PygmentsLexer(PythonLexer)) self.history_buffer_control = BufferControl( buffer=history.history_buffer, lexer=PygmentsLexer(PythonLexer), search_buffer_control=search_toolbar.control, preview_search=True) history_window = Window( content=self.history_buffer_control, wrap_lines=False, left_margins=[HistoryMargin(history)], scroll_offsets=ScrollOffsets(top=2, bottom=2)) self.root_container = HSplit([ # Top title bar. Window( content=FormattedTextControl(_get_top_toolbar_fragments), align=WindowAlign.CENTER, style='class:status-toolbar'), FloatContainer( content=VSplit([ # Left side: history. history_window, # Separator. Window(width=D.exact(1), char=BORDER.LIGHT_VERTICAL, style='class:separator'), # Right side: result. Window( content=self.default_buffer_control, wrap_lines=False, left_margins=[ResultMargin(history)], scroll_offsets=ScrollOffsets(top=2, bottom=2)), ]), floats=[ # Help text as a float. Float(width=60, top=3, bottom=2, content=ConditionalContainer( content=help_window, filter=has_focus(history.help_buffer))), ] ), # Bottom toolbars. ArgToolbar(), search_toolbar, Window( content=FormattedTextControl( partial(_get_bottom_toolbar_fragments, history=history)), style='class:status-toolbar'), ]) self.layout = Layout(self.root_container, history_window)
def create_layout(python_input, history_mapping): """ Create and return a `Container` instance for the history application. """ processors = [ HighlightSearchProcessor(preview_search=True), HighlightSelectionProcessor() ] help_window = create_popup_window( title='History Help', body=Window(content=BufferControl(buffer_name=HELP_BUFFER, default_char=Char(token=Token), lexer=PygmentsLexer(RstLexer), input_processors=processors), right_margins=[ScrollbarMargin()], scroll_offsets=ScrollOffsets(top=2, bottom=2))) return HSplit([ # Top title bar. TokenListToolbar(get_tokens=_get_top_toolbar_tokens, align_center=True, default_char=Char(' ', Token.Toolbar.Status)), FloatContainer( content=VSplit([ # Left side: history. Window(content=BufferControl(buffer_name=HISTORY_BUFFER, lexer=PygmentsLexer(PythonLexer), input_processors=processors), wrap_lines=False, left_margins=[HistoryMargin(history_mapping)], scroll_offsets=ScrollOffsets(top=2, bottom=2)), # Separator. Window(width=D.exact(1), content=FillControl(BORDER.LIGHT_VERTICAL, token=Token.Separator)), # Right side: result. Window( content=BufferControl(buffer_name=DEFAULT_BUFFER, input_processors=processors + [GrayExistingText(history_mapping)], lexer=PygmentsLexer(PythonLexer)), wrap_lines=False, left_margins=[ResultMargin(history_mapping)], scroll_offsets=ScrollOffsets(top=2, bottom=2)), ]), floats=[ # Help text as a float. Float( width=60, top=3, bottom=2, content=ConditionalContainer( # (We use InFocusStack, because it's possible to search # through the help text as well, and at that point the search # buffer has the focus.) content=help_window, filter=InFocusStack(HELP_BUFFER))), ]), # Bottom toolbars. ArgToolbar(), SearchToolbar(), TokenListToolbar(get_tokens=partial(_get_bottom_toolbar_tokens, python_input=python_input), default_char=Char(' ', Token.Toolbar.Status)), ])
def python_sidebar(python_input: "PythonInput") -> Window: """ Create the `Layout` for the sidebar with the configurable options. """ def get_text_fragments() -> StyleAndTextTuples: tokens: StyleAndTextTuples = [] def append_category(category: "OptionCategory") -> None: tokens.extend([ ("class:sidebar", " "), ("class:sidebar.title", " %-36s" % category.title), ("class:sidebar", "\n"), ]) def append(index: int, label: str, status: str) -> None: selected = index == python_input.selected_option_index @if_mousedown def select_item(mouse_event: MouseEvent) -> None: python_input.selected_option_index = index @if_mousedown def goto_next(mouse_event: MouseEvent) -> None: " Select item and go to next value. " python_input.selected_option_index = index option = python_input.selected_option option.activate_next() sel = ",selected" if selected else "" tokens.append(("class:sidebar" + sel, " >" if selected else " ")) tokens.append( ("class:sidebar.label" + sel, "%-24s" % label, select_item)) tokens.append(("class:sidebar.status" + sel, " ", select_item)) tokens.append( ("class:sidebar.status" + sel, "%s" % status, goto_next)) if selected: tokens.append(("[SetCursorPosition]", "")) tokens.append(("class:sidebar.status" + sel, " " * (13 - len(status)), goto_next)) tokens.append(("class:sidebar", "<" if selected else "")) tokens.append(("class:sidebar", "\n")) i = 0 for category in python_input.options: append_category(category) for option in category.options: append(i, option.title, "%s" % option.get_current_value()) i += 1 tokens.pop() # Remove last newline. return tokens class Control(FormattedTextControl): def move_cursor_down(self): python_input.selected_option_index += 1 def move_cursor_up(self): python_input.selected_option_index -= 1 return Window( Control(get_text_fragments), style="class:sidebar", width=Dimension.exact(43), height=Dimension(min=3), scroll_offsets=ScrollOffsets(top=1, bottom=1), )
def question(message, **kwargs): # TODO extract common parts for list, checkbox, rawlist, expand if not 'choices' in kwargs: raise PromptParameterException('choices') # this does not implement default, use checked... # TODO # if 'default' in kwargs: # raise ValueError('rawlist does not implement \'default\' ' # 'use \'checked\':True\' in choice!') qmark = kwargs.pop('qmark', '?') choices = kwargs.pop('choices', None) if len(choices) > 9: raise ValueError('rawlist supports only a maximum of 9 choices!') # TODO style defaults on detail level style = kwargs.pop('style', default_style) ic = InquirerControl(choices) def get_prompt_tokens(): tokens = [] tokens.append(('class:questionmark', qmark)) tokens.append(('class:question', ' %s ' % message)) if ic.answered: tokens.append(('class:answer', ' %s' % ic.get_selected_value())) return tokens # assemble layout layout = HSplit([ Window(height=D.exact(1), content=FormattedTextControl(get_prompt_tokens) ), ConditionalContainer( Window(ic), filter=~IsDone() ) ]) # key bindings kb = KeyBindings() @kb.add('c-q', eager=True) @kb.add('c-c', eager=True) def _(event): raise KeyboardInterrupt() # add key bindings for choices for i, c in enumerate(ic.choices): if not isinstance(c, Separator): def _reg_binding(i, keys): # trick out late evaluation with a "function factory": # http://stackoverflow.com/questions/3431676/creating-functions-in-a-loop @kb.add(keys, eager=True) def select_choice(event): ic.pointer_index = i _reg_binding(i, '%d' % c[0]) @kb.add('enter', eager=True) def set_answer(event): ic.answered = True event.app.exit(result=ic.get_selected_value()) return Application( layout=Layout(layout), key_bindings=kb, mouse_support=True, style=style )
dignissim placerat. In vel dictum ex, vulputate accumsan mi. Donec ut quam placerat massa tempor elementum. Sed tristique mauris ac suscipit euismod. Ut tempus vehicula augue non venenatis. Mauris aliquam velit turpis, nec congue risus aliquam sit amet. Pellentesque blandit scelerisque felis, faucibus consequat ante. Curabitur tempor tortor a imperdiet tincidunt. Nam sed justo sit amet odio bibendum congue. Quisque varius ligula nec ligula gravida, sed convallis augue faucibus. Nunc ornare pharetra bibendum. Praesent blandit ex quis sodales maximus. """ * 100).split()) # 1. The layout left_text = HTML("<reverse>transparent=False</reverse>\n") right_text = HTML("<reverse>transparent=True</reverse>") quit_text = "Press 'q' to quit." body = FloatContainer( content=Window(FormattedTextControl(LIPSUM), wrap_lines=True), floats=[ # Important note: Wrapping the floating objects in a 'Frame' is # only required for drawing the border around the # floating text. We do it here to make the layout more # obvious. # Left float. Float(Frame(Window(FormattedTextControl(left_text), width=20, height=4)), transparent=False, left=0), # Right float. Float(Frame(
risus aliquam sit amet. Pellentesque blandit scelerisque felis, faucibus consequat ante. Curabitur tempor tortor a imperdiet tincidunt. Nam sed justo sit amet odio bibendum congue. Quisque varius ligula nec ligula gravida, sed convallis augue faucibus. Nunc ornare pharetra bibendum. Praesent blandit ex quis sodales maximus.""" # Create text buffers. Cursorcolumn/cursorline are mostly combined with an # (editable) text buffers, where the user can move the cursor. buff = Buffer() buff.text = LIPSUM # 1. The layout body = HSplit([ Window(FormattedTextControl('Press "q" to quit.'), height=1, style='reverse'), Window(BufferControl(buffer=buff), cursorcolumn=True, cursorline=True), ]) # 2. Key bindings kb = KeyBindings() @kb.add('q') def _(event): " Quit application. " event.app.exit() # 3. The `Application`
ctrls = myControls() myButtons = list() threadsDir = "threads" + os.sep dirs = os.listdir(threadsDir) for d in dirs: myButtons.append(myButton(d, myControls=ctrls, name=d)) body = VSplit([ HSplit(myButtons), # A vertical line in the middle. We explicitly specify the width, to make # sure that the layout engine will not try to divide the whole width by # three for all these windows. Window(width=1, char='|', style='class:line'), #Window(content) HSplit( list([ Window(ctrls.content), Window(height=1, char='=', style='class:line'), ctrls.sender ])) ]) # 2. Key bindings kb = KeyBindings() # Key bindings. kb = KeyBindings() kb.add('tab')(focus_next)
def get_cell(cell): def _(): return HTML(controller.get_cell_html(cell)) return _ cell_input_dialog = Dialog( TextArea(accept_handler=commit_changes, multiline=False)) controller = PysheetApplication(cell_input_dialog) grid_container = HSplit([ # Header for the columns name VSplit([ Window(FormattedTextControl(), width=10), *[ Window(FormattedTextControl(get_column_name(column)), width=10) for column in controller.view.visible_columns() ] ], height=1), # Display each row HSplit([ VSplit([ Window(FormattedTextControl(HTML(str(row))), width=2), *[ Window(FormattedTextControl(get_cell(cell)), width=10) for cell in controller.view.get_visible_cells(row, row=True) ] ]) for row in controller.view.visible_rows() ], height=controller.view.rows_to_show),
from prompt_toolkit import ANSI from prompt_toolkit.buffer import Buffer from prompt_toolkit.widgets import Frame from prompt_toolkit.layout.containers import Window from prompt_toolkit.layout.controls import BufferControl, FormattedTextControl from prompt_toolkit.lexers import PygmentsLexer from pygments.lexers.python import PythonLexer # type: ignore from rich.syntax import Syntax from rich.markdown import Markdown from rich.console import Console # TODO: take language into account lexer: PygmentsLexer = PygmentsLexer(PythonLexer) EMPTY_PREFIX: Window = Window(width=10) console = Console() def get_output_text_and_height(outputs: List[Dict[str, Any]]): text_list = [] height = 0 for output in outputs: if output["output_type"] == "stream": text = "".join(output["text"]) height += text.count("\n") or 1 if output["name"] == "stderr": with console.capture() as capture: console.print(text, style="white on red", end="") text = capture.get()
def question(message, **kwargs): default = kwargs.pop('default', True) status = {'answer': None} qmark = kwargs.pop('qmark', '?') def get_prompt_tokens(): tokens = [] tokens.append(('class:question-mark', qmark)) tokens.append(('class:question', ' %s ' % message)) if isinstance(status['answer'], bool): tokens.append( ('class:answer', ' Yes' if status['answer'] else ' No')) else: if default: instruction = ' (Y/n)' else: instruction = ' (y/N)' tokens.append(('class:instruction', instruction)) return tokens # key bindings kb = KeyBindings() @kb.add(Keys.ControlQ, eager=True) @kb.add(Keys.ControlC, eager=True) def _(event): event.app.exit(exception=KeyboardInterrupt()) @kb.add('n') @kb.add('N') def key_n(event): # pylint:disable=unused-variable status['answer'] = False event.app.exit(result=False) @kb.add('y') @kb.add('Y') def key_y(event): # pylint:disable=unused-variable status['answer'] = True event.app.exit(result=True) @kb.add(Keys.Enter, eager=True) def set_answer(event): # pylint:disable=unused-variable status['answer'] = default event.app.exit(result=default) # assemble layout layout = Layout( HSplit([ Window(height=D.exact(1), content=FormattedTextControl(get_prompt_tokens)), ])) return Application( layout=layout, key_bindings=kb, mouse_support=False, style=default_style, )
def run(): line_cursor_pos = 0 current_line = 0 cursor_lock = Lock() during_accept = False # The layout. output_field = TextArea(style="class:output-field") input_field = TextArea( height=1, prompt=" # ", style="class:input-field", multiline=False, wrap_lines=False, ) roll = ['\\', '-', '/', '|'] current_roll = -1 def get_statusbar_text(): nonlocal during_accept, current_roll if during_accept: if current_roll == 3: current_roll = -1 current_roll = current_roll + 1 return ' {} Searching...'.format(roll[current_roll]) else: return ' = Nice Translator' def get_statusbar_class(): nonlocal during_accept if during_accept: return "class:searching-status" else: return "class:status" container = HSplit([ input_field, Window(height=1, char="-", style="class:line"), output_field, Window(FormattedTextControl(get_statusbar_text), height=1, style=get_statusbar_class), ]) def addline(old, newline): return old + ' {}\n'.format(newline) def accept(buff): with cursor_lock: nonlocal during_accept during_accept = True nonlocal line_cursor_pos nonlocal current_line line_cursor_pos = 0 current_line = 0 new_text = '' if input_field.text.strip() != '': try: translate_result = translate.translate(input_field.text) output = '' for rs in translate_result: output = addline(output, rs) text = output text_lines = text.splitlines(keepends=True) for i, l in enumerate(text_lines): if (l.startswith(' > ')): text_lines[i] = ' ' + text_lines[i][3:] break text = ''.join(text_lines) new_text = ' > ' + text[3:] except Exception as e: new_text = 'Some error occured.\n{}'.format(e) # Add text to output buffer. output_field.buffer.document = Document( text=new_text, cursor_position=line_cursor_pos) during_accept = False def threading_accept(buff): l = [buff] t = Thread(target=accept, args=l) t.start() input_field.accept_handler = threading_accept # The key bindings. kb = KeyBindings() # show where the cursor is # @kb.add("tab") # def _(event): # event.app.layout.focus_next() @kb.add('escape', 'q') def _(event): " Pressing Ctrl-Q or Ctrl-C will exit the user interface. " event.app.exit() # cursor move next line @kb.add('escape', 'n') def _(event): with cursor_lock: nonlocal line_cursor_pos nonlocal current_line new_text = '' lines = output_field.text.splitlines(keepends=True) if len(lines) == 0 or current_line is len(lines) - 1: return for i, l in enumerate(lines): if i == current_line: lines[i] = ' ' + lines[i][3:] lines[i + 1] = ' > ' + lines[i + 1][3:] new_text = new_text + lines[i] line_cursor_pos = line_cursor_pos + len(lines[current_line]) current_line = current_line + 1 output_field.buffer.document = Document( text=new_text, cursor_position=line_cursor_pos) # cursor move previous line @kb.add('escape', 'm') def _(event): with cursor_lock: nonlocal line_cursor_pos nonlocal current_line new_text = '' lines = output_field.text.splitlines(keepends=True) if current_line is 0: return for i, l in enumerate(lines): if i == current_line: lines[i] = ' ' + lines[i][3:] lines[i - 1] = ' > ' + lines[i - 1][3:] break for l in lines: new_text = new_text + l line_cursor_pos = line_cursor_pos - len(lines[current_line - 1]) current_line = current_line - 1 output_field.buffer.document = Document( text=new_text, cursor_position=line_cursor_pos) # Style. style = Style([("output-field", ""), ("input-field", ""), ("line", "#ffffff"), ("status", "bg:grey #fff"), ("searching-status", "bg:purple #fff")]) # Run application. application = Application(layout=Layout(container), key_bindings=kb, style=style, full_screen=True, refresh_interval=0.3) application.run()
def create_layout(python_input, key_bindings_manager, lexer=PythonLexer, extra_body=None, extra_toolbars=None, extra_buffer_processors=None, input_buffer_height=None): D = LayoutDimension extra_body = [extra_body] if extra_body else [] extra_toolbars = extra_toolbars or [] extra_buffer_processors = extra_buffer_processors or [] input_buffer_height = input_buffer_height or D(min=6) def create_python_input_window(): def menu_position(cli): """ When there is no autocompletion menu to be shown, and we have a signature, set the pop-up position at `bracket_start`. """ b = cli.buffers[DEFAULT_BUFFER] if b.complete_state is None and python_input.signatures: row, col = python_input.signatures[0].bracket_start index = b.document.translate_row_col_to_index(row - 1, col) return index return Window( BufferControl( buffer_name=DEFAULT_BUFFER, lexer=lexer, input_processors=[ ConditionalProcessor( processor=HighlightSearchProcessor( preview_search=True), filter=HasFocus(SEARCH_BUFFER), ), HighlightSelectionProcessor(), DisplayMultipleCursors(DEFAULT_BUFFER), # Show matching parentheses, but only while editing. ConditionalProcessor( processor=HighlightMatchingBracketProcessor( chars='[](){}'), filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() & Condition(lambda cli: python_input. highlight_matching_parenthesis)), ConditionalProcessor(processor=AppendAutoSuggestion(), filter=~IsDone()) ] + extra_buffer_processors, menu_position=menu_position, # Make sure that we always see the result of an reverse-i-search: preview_search=True, ), left_margins=[PythonPromptMargin(python_input)], # Scroll offsets. The 1 at the bottom is important to make sure the # cursor is never below the "Press [Meta+Enter]" message which is a float. scroll_offsets=ScrollOffsets(bottom=1, left=4, right=4), # As long as we're editing, prefer a minimal height of 6. get_height=(lambda cli: (None if cli.is_done or python_input. show_exit_confirmation else input_buffer_height)), wrap_lines=Condition(lambda cli: python_input.wrap_lines), ) return HSplit([ VSplit([ HSplit([ FloatContainer( content=HSplit([create_python_input_window()] + extra_body), floats=[ Float(xcursor=True, ycursor=True, content=CompletionsMenu( scroll_offset=Integer.from_callable( lambda: python_input. completion_menu_scroll_offset), max_height=12, extra_filter=show_completions_menu( python_input))), Float( xcursor=True, ycursor=True, content=MultiColumnCompletionsMenu( extra_filter=show_multi_column_completions_menu( python_input))), Float(xcursor=True, ycursor=True, content=signature_toolbar(python_input)), Float(left=2, bottom=1, content=exit_confirmation(python_input)), Float(bottom=0, right=0, height=1, content=meta_enter_message(python_input), hide_when_covering_content=True), Float(bottom=1, left=1, right=0, content=python_sidebar_help(python_input)), ]), ArgToolbar(), SearchToolbar(), SystemToolbar(), ValidationToolbar(), CompletionsToolbar( extra_filter=show_completions_toolbar(python_input)), # Docstring region. ConditionalContainer(content=Window( height=D.exact(1), content=FillControl('\u2500', token=Token.Separator)), filter=HasSignature(python_input) & ShowDocstring(python_input) & ~IsDone()), ConditionalContainer( content=Window( BufferControl( buffer_name='docstring', lexer=SimpleLexer(default_token=Token.Docstring), #lexer=PythonLexer, ), height=D(max=12)), filter=HasSignature(python_input) & ShowDocstring(python_input) & ~IsDone(), ), ]), HSplit([ python_sidebar(python_input), python_sidebar_navigation(python_input), ]) ]), ] + extra_toolbars + [ VSplit([ status_bar(key_bindings_manager, python_input), show_sidebar_button_info(python_input), ]) ])
Label(text=text), ]), buttons=[ok_button], width=D(preferred=80), modal=True) def __pt_container__(self): return self.dialog body = HSplit([ text_field, search_toolbar, ConditionalContainer( content=VSplit([ Window(FormattedTextControl(get_statusbar_text), style='class:status'), Window(FormattedTextControl(get_statusbar_right_text), style='class:status.right', width=9, align=WindowAlign.RIGHT), ], height=1), filter=Condition(lambda: ApplicationState.show_status_bar)), ]) # Global key bindings. bindings = KeyBindings() @bindings.add('c-c') def _(event): " Focus menu. " event.app.layout.focus(root_container.window)
def start_app(args): """Text-based GUI application""" cmd = Commands() completer = WordCompleter(cmd.commands(), meta_dict=cmd.meta_dict(), ignore_case=True) history = InMemoryHistory() # Individual windows input_field = TextArea(height=1, prompt='ctserial> ', style='class:input-field', completer=completer, history=history) output_field = TextArea(scrollbar=True, style='class:output-field', text='') statusbar = Window(content=FormattedTextControl(get_statusbar_text), height=1, style='class:statusbar') # Organization of windows body = FloatContainer(HSplit([ input_field, Window(height=1, char='-', style='class:line'), output_field, statusbar ]), floats=[ Float(xcursor=True, ycursor=True, content=CompletionsMenu(max_height=16, scroll_offset=1)) ]) # Adding menus root_container = MenuContainer( body=body, menu_items=[], # menu_items=[ # MenuItem('Project ', children=[ # MenuItem('New'), # MenuItem('Open'), # MenuItem('Save'), # MenuItem('Save as...'), # MenuItem('-', disabled=True), # MenuItem('Exit'), ]), # MenuItem('View ', children=[ # MenuItem('Split'), ]), # MenuItem('Info ', children=[ # MenuItem('Help'), # MenuItem('About'), ]), ], floats=[ Float(xcursor=True, ycursor=True, content=CompletionsMenu(max_height=16, scroll_offset=1)), ]) # The key bindings. kb = KeyBindings() @kb.add('space') def _(event): input_text = input_field.text cursor = len(input_text) input_updated = input_text[:cursor] + ' ' + input_text[cursor + 1:] cursor += 1 input_field.buffer.document = Document(text=input_updated, cursor_position=cursor) input_field.buffer.completer = WordCompleter([], ignore_case=True) @kb.add('enter', filter=has_focus(input_field)) def _(event): # Process commands on prompt after hitting enter key # tx_bytes = parse_command(input_field.text, event=event) input_field.buffer.completer = WordCompleter(cmd.commands(), meta_dict=cmd.meta_dict(), ignore_case=True) if len(input_field.text) == 0: return output_text = cmd.execute(input_field.text, output_field.text, event) input_field.buffer.reset(append_to_history=True) # For commands that do not send data to serial device if output_text == None: input_field.text = '' return # For invalid commands forcing users to correct them elif output_text == False: return # For invalid commands forcing users to correct them else: output_field.buffer.document = Document( text=output_text, cursor_position=len(output_text)) input_field.text = '' @kb.add('c-c') def _(event): """Pressing Control-C will copy highlighted text to clipboard""" data = output_field.buffer.copy_selection() get_app().clipboard.set_data(data) @kb.add('c-p') def _(event): """Pressing Control-P will paste text from clipboard""" input_field.buffer.paste_clipboard_data(get_app().clipboard.get_data()) @kb.add('c-q') def _(event): " Pressing Ctrl-Q will exit the user interface. " cmd.do_exit(input_field.text, output_field.text, event) @kb.add('c-d') def _(event): """Press Ctrl-D to start the python debugger""" import pdb pdb.set_trace() style = Style([ # ('output-field', 'bg:#000000 #ffffff'), # ('input-field', 'bg:#000000 #ffffff'), ('line', '#004400'), ('statusbar', 'bg:#AAAAAA') ]) # Run application. application = MyApplication(layout=Layout(root_container, focused_element=input_field), key_bindings=kb, style=style, mouse_support=True, full_screen=True) application.run()
def question(message, **kwargs): # TODO disabled, dict choices if not 'choices' in kwargs: raise PromptParameterException('choices') choices = kwargs.pop('choices', None) default = kwargs.pop('default', None) qmark = kwargs.pop('qmark', '?') # TODO style defaults on detail level style = kwargs.pop('style', default_style) ic = InquirerControl(choices, default=default) def get_prompt_tokens(cli): tokens = [] tokens.append((Token.QuestionMark, qmark)) tokens.append((Token.Question, ' %s ' % message)) if ic.answered: tokens.append((Token.Answer, ' ' + ic.get_selection()[0])) else: tokens.append((Token.Instruction, ' (Use arrow keys)')) return tokens # assemble layout layout = HSplit([ Window(height=D.exact(1), content=TokenListControl(get_prompt_tokens)), ConditionalContainer(Window(ic), filter=~IsDone()) ]) # key bindings manager = KeyBindingManager.for_prompt() @manager.registry.add_binding(Keys.ControlQ, eager=True) @manager.registry.add_binding(Keys.ControlC, eager=True) def _(event): raise KeyboardInterrupt() # event.cli.set_return_value(None) @manager.registry.add_binding(Keys.Down, eager=True) def move_cursor_down(event): def _next(): ic.selected_option_index = ((ic.selected_option_index + 1) % ic.choice_count) _next() while isinstance(ic.choices[ic.selected_option_index][0], Separator) or\ ic.choices[ic.selected_option_index][2]: _next() @manager.registry.add_binding(Keys.Up, eager=True) def move_cursor_up(event): def _prev(): ic.selected_option_index = ((ic.selected_option_index - 1) % ic.choice_count) _prev() while isinstance(ic.choices[ic.selected_option_index][0], Separator) or \ ic.choices[ic.selected_option_index][2]: _prev() @manager.registry.add_binding(Keys.Enter, eager=True) def set_answer(event): ic.answered = True event.cli.set_return_value(ic.get_selection()[1]) return Application(layout=layout, key_bindings_registry=manager.registry, mouse_support=True, style=style)
Window, WindowAlign, ) from prompt_toolkit.layout.controls import BufferControl, FormattedTextControl from prompt_toolkit.layout.layout import Layout # 3. Create the buffers # ------------------ left_buffer = Buffer() right_buffer = Buffer() # 1. First we create the layout # -------------------------- left_window = Window(BufferControl(buffer=left_buffer)) right_window = Window(BufferControl(buffer=right_buffer)) body = VSplit([ left_window, # A vertical line in the middle. We explicitly specify the width, to make # sure that the layout engine will not try to divide the whole width by # three for all these windows. Window(width=1, char='|', style='class:line'), # Display the Result buffer on the right. right_window, ]) # As a demonstration. Let's add a title bar to the top, displaying "Hello world".
def question(message, **kwargs): # TODO need ENTER confirmation default = kwargs.pop('default', True) # TODO style defaults on detail level style = kwargs.pop( 'style', style_from_dict({ Token.QuestionMark: '#5F819D', #Token.Selected: '#FF9D00', # AWS orange Token.Instruction: '', # default Token.Answer: '#FF9D00 bold', # AWS orange Token.Question: 'bold', })) status = {'answer': None} def get_prompt_tokens(cli): tokens = [] T = Token tokens.append((Token.QuestionMark, '?')) tokens.append((Token.Question, ' %s ' % message)) if isinstance(status['answer'], bool): tokens.append( (Token.Answer, ' Yes' if status['answer'] else ' No')) else: if default: instruction = ' (Y/n)' else: instruction = ' (y/N)' tokens.append((Token.Instruction, instruction)) return tokens # assemble layout # TODO this does not work without the HSplit?? layout = HSplit([ Window( height=D.exact(1), content=TokenListControl(get_prompt_tokens, align_center=False), ) ]) # key bindings manager = KeyBindingManager.for_prompt() @manager.registry.add_binding(Keys.ControlQ, eager=True) @manager.registry.add_binding(Keys.ControlC, eager=True) def _(event): raise KeyboardInterrupt() @manager.registry.add_binding('n') @manager.registry.add_binding('N') def key_n(event): status['answer'] = False event.cli.set_return_value(False) @manager.registry.add_binding('y') @manager.registry.add_binding('Y') def key_y(event): status['answer'] = True event.cli.set_return_value(True) @manager.registry.add_binding(Keys.Enter, eager=True) def set_answer(event): status['answer'] = default event.cli.set_return_value(default) return Application( layout=layout, key_bindings_registry=manager.registry, mouse_support=False, style=style, erase_when_done=False, )
placerat massa tempor elementum. Sed tristique mauris ac suscipit euismod. Ut tempus vehicula augue non venenatis. Mauris aliquam velit turpis, nec congue risus aliquam sit amet. Pellentesque blandit scelerisque felis, faucibus consequat ante. Curabitur tempor tortor a imperdiet tincidunt. Nam sed justo sit amet odio bibendum congue. Quisque varius ligula nec ligula gravida, sed convallis augue faucibus. Nunc ornare pharetra bibendum. Praesent blandit ex quis sodales maximus.""" # 1. The layout left_text = '\nLeft aligned text. - (Press "q" to quit)\n\n' + LIPSUM center_text = "Centered text.\n\n" + LIPSUM right_text = "Right aligned text.\n\n" + LIPSUM body = HSplit([ Window(FormattedTextControl(left_text), align=WindowAlign.LEFT), Window(height=1, char="-"), Window(FormattedTextControl(center_text), align=WindowAlign.CENTER), Window(height=1, char="-"), Window(FormattedTextControl(right_text), align=WindowAlign.RIGHT), ]) # 2. Key bindings kb = KeyBindings() @kb.add("q") def _(event): " Quit application. " event.app.exit()
def status_bar(python_input: "PythonInput") -> Container: """ Create the `Layout` for the status bar. """ TB = "class:status-toolbar" @if_mousedown def toggle_paste_mode(mouse_event: MouseEvent) -> None: python_input.paste_mode = not python_input.paste_mode @if_mousedown def enter_history(mouse_event: MouseEvent) -> None: python_input.enter_history() def get_text_fragments() -> StyleAndTextTuples: python_buffer = python_input.default_buffer result: StyleAndTextTuples = [] append = result.append append((TB, " ")) result.extend(get_inputmode_fragments(python_input)) append((TB, " ")) # Position in history. append(( TB, "%i/%i " % (python_buffer.working_index + 1, len(python_buffer._working_lines)), )) # Shortcuts. app = get_app() if (not python_input.vi_mode and app.current_buffer == python_input.search_buffer): append((TB, "[Ctrl-G] Cancel search [Enter] Go to this position.")) elif bool(app.current_buffer.selection_state ) and not python_input.vi_mode: # Emacs cut/copy keys. append( (TB, "[Ctrl-W] Cut [Meta-W] Copy [Ctrl-Y] Paste [Ctrl-G] Cancel")) else: result.extend([ (TB + " class:status-toolbar.key", "[F3]", enter_history), (TB, " History ", enter_history), (TB + " class:status-toolbar.key", "[F6]", toggle_paste_mode), (TB, " ", toggle_paste_mode), ]) if python_input.paste_mode: append((TB + " class:paste-mode-on", "Paste mode (on)", toggle_paste_mode)) else: append((TB, "Paste mode", toggle_paste_mode)) return result return ConditionalContainer( content=Window(content=FormattedTextControl(get_text_fragments), style=TB), filter=~is_done & renderer_height_is_known & Condition(lambda: python_input.show_status_bar and not python_input. show_exit_confirmation), )