def main(): style = Style([ ('terminal not-focused', '#888888'), ('title', 'bg:#000044 #ffffff underline'), ]) done_count = [0] # nonlocal. def done(): done_count[0] += 1 if done_count[0] == 2: application.exit() else: switch_focus() term1 = Terminal(width=D(preferred=80), height=D(preferred=40), style='class:terminal', done_callback=done) term2 = Terminal(width=D(preferred=80), height=D(preferred=40), style='class:terminal', done_callback=done) kb = KeyBindings() @kb.add('c-w') def _(event): switch_focus() def switch_focus(): " Change focus when Control-W is pressed." if application.layout.has_focus(term1): application.layout.focus(term2) else: application.layout.focus(term1) application = Application( layout=Layout(container=HSplit([ Window( height=1, style='class:title', content=FormattedTextControl( HTML( ' Press <u fg="#ff8888"><b>Control-W</b></u> to <b>switch focus</b>.' ))), VSplit([ term1, Window(style='bg:#aaaaff', width=1), term2, ]), ]), focused_element=term1), style=style, key_bindings=kb, full_screen=True, mouse_support=True, ) application.run()
def __init__(self, title='', label_text='', completer=None): self.future = Future() def accept_text(): get_app().layout.focus(ok_button) self.text_area.buffer.complete_state = None def accept(): self.future.set_result(self.text_area.text) def cancel(): self.future.set_result(None) self.text_area = TextArea( completer=completer, multiline=False, width=D(preferred=40), accept_handler=accept_text) ok_button = Button(text='OK', handler=accept) cancel_button = Button(text='Cancel', handler=cancel) self.dialog = Dialog( title=title, body=HSplit([ Label(text=label_text), self.text_area ]), buttons=[ok_button, cancel_button], width=D(preferred=80), modal=True)
def get(): result = [] # Padding left. if self.align in (HorizontalAlign.CENTER, HorizontalAlign.RIGHT): result.append(Window(width=D(preferred=0))) # Border left is first inserted in children loop. # The children with padding. c = 0 for child in self.children: result.append(_VerticalBorder(borders=self.borders)) result.append(child) c += child.merge # Fill in any missing columns for _ in range(self.columns - c): result.append(_VerticalBorder(borders=self.borders)) result.append(_Cell(cell=None, table=self.table, row=self)) # Border right. result.append(_VerticalBorder(borders=self.borders)) # Padding right. if self.align in (HorizontalAlign.CENTER, HorizontalAlign.LEFT): result.append(Window(width=D(preferred=0))) return result
def get(): result = [] # Padding top. if self.align in (VerticalAlign.CENTER, VerticalAlign.BOTTOM): result.append(Window(width=D(preferred=0))) # Border top is first inserted in children loop. # The children with padding. prev = None for child in self.children: result.append(_Border( prev=prev, next=child, table=self, borders=self.borders)) result.append(child) prev = child # Border bottom. result.append(_Border(prev=prev, next=None, table=self, borders=self.borders)) # Padding bottom. if self.align in (VerticalAlign.CENTER, VerticalAlign.TOP): result.append(Window(width=D(preferred=0))) return result
def __init__(self, title="", label_text="", completer=None): self.future = Future() def accept_text(buf): app.layout.focus(ok_button) buf.complete_state = None return True def accept(): self.future.set_result(self.text_area.text) def cancel(): self.future.set_result(None) self.text_area = TextArea( completer=completer, multiline=False, width=D(preferred=40), accept_handler=accept_text, ) ok_button = Button(text="OK", handler=accept) cancel_button = Button(text="Cancel", handler=cancel) self.dialog = Dialog( title=title, body=HSplit([Label(text=label_text), self.text_area]), buttons=[ok_button, cancel_button], width=D(preferred=80), modal=True, )
def __init__(self) -> None: self._percentage = 60 self.label = Label('60%') self.container = FloatContainer( content=Window(height=1), floats=[ # We first draw the label, then the actual progress bar. Right # now, this is the only way to have the colors of the progress # bar appear on top of the label. The problem is that our label # can't be part of any `Window` below. Float(content=self.label, top=0, bottom=0), Float(left=0, top=0, right=0, bottom=0, content=VSplit([ Window( style='class:progress-bar.used', width=lambda: D(weight=int(self._percentage))), Window(style='class:progress-bar', width=lambda: D(weight=int(100 - self. _percentage))), ])), ])
def __init__(self, body: AnyContainer, title: AnyFormattedText = '', buttons: Optional[Sequence[Button]] = None, modal: bool = True, width: AnyDimension = None, with_background: bool = False) -> None: self.body = body self.title = title buttons = buttons or [] # When a button is selected, handle left/right key bindings. buttons_kb = KeyBindings() if len(buttons) > 1: first_selected = has_focus(buttons[0]) last_selected = has_focus(buttons[-1]) buttons_kb.add('left', filter=~first_selected)(focus_previous) buttons_kb.add('right', filter=~last_selected)(focus_next) frame_body: AnyContainer if buttons: frame_body = HSplit([ # Add optional padding around the body. Box(body=DynamicContainer(lambda: self.body), padding=D(preferred=1, max=1), padding_bottom=0), # The buttons. Box(body=VSplit(buttons, padding=1, key_bindings=buttons_kb), height=D(min=1, max=3, preferred=3)) ]) else: frame_body = body # Key bindings for whole dialog. kb = KeyBindings() kb.add('tab', filter=~has_completions)(focus_next) kb.add('s-tab', filter=~has_completions)(focus_previous) frame = Shadow(body=Frame( title=lambda: self.title, body=frame_body, style='class:dialog.body', width=(None if with_background is None else width), key_bindings=kb, modal=modal, )) self.container: Union[Box, Shadow] if with_background: self.container = Box( body=frame, style='class:dialog', width=width) else: self.container = frame
def get_width(self, progress_bar): if self.width: return self.width all_names = [self._add_suffix(c.label) for c in progress_bar.counters] if all_names: max_widths = max(get_cwidth(name) for name in all_names) return D(preferred=max_widths, max=max_widths) else: return D()
def get_width(self, progress_bar): if self.width: return self.width all_labels = [self._add_suffix(c.label) for c in progress_bar.counters] if all_labels: max_widths = max(fragment_list_width(l) for l in all_labels) return D(preferred=max_widths, max=max_widths) else: return D()
def get_width() -> AnyDimension: if width is None: text_fragments = to_formatted_text(self.text) text = fragment_list_to_text(text_fragments) if text: longest_line = max(get_cwidth(line) for line in text.splitlines()) else: return D(preferred=0) return D(preferred=longest_line) else: return width
def __init__(self, body, title='', buttons=None, modal=True, width=None, with_background=False): assert is_formatted_text(title) assert buttons is None or isinstance(buttons, list) buttons = buttons or [] # When a button is selected, handle left/right key bindings. buttons_kb = KeyBindings() if len(buttons) > 1: first_selected = has_focus(buttons[0]) last_selected = has_focus(buttons[-1]) buttons_kb.add('left', filter=~first_selected)(focus_previous) buttons_kb.add('right', filter=~last_selected)(focus_next) if buttons: frame_body = HSplit([ # Add optional padding around the body. Box(body=body, padding=D(preferred=1, max=1), padding_bottom=0), # The buttons. Box(body=VSplit(buttons, padding=1, key_bindings=buttons_kb), height=D(min=1, max=3, preferred=3)) ]) else: frame_body = body # Key bindings for whole dialog. kb = KeyBindings() kb.add('tab', filter=~has_completions)(focus_next) kb.add('s-tab', filter=~has_completions)(focus_previous) frame = Shadow(body=Frame( title=title, body=frame_body, style='class:dialog.body', width=(None if with_background is None else width), key_bindings=kb, modal=modal, )) if with_background: self.container = Box(body=frame, style='class:dialog', width=width) else: self.container = frame
def get(): result = [] # Padding left. if self.align in (HorizontalAlign.CENTER, HorizontalAlign.RIGHT): result.append(Window(width=D(preferred=0))) def char(i, pc=False, nc=False): if i == 0: if self.prev and self.next: return self.borders.LEFT_T elif self.prev: return self.borders.BOTTOM_LEFT else: return self.borders.TOP_LEFT if i == self.columns: if self.prev and self.next: return self.borders.RIGHT_T elif self.prev: return self.borders.BOTTOM_RIGHT else: return self.borders.TOP_RIGHT if pc and nc: return self.borders.INTERSECT elif pc: return self.borders.BOTTOM_T elif nc: return self.borders.TOP_T else: return self.borders.HORIZONTAL # Border left is first inserted in children loop. # The children with padding. pcs = self.has_borders(self.prev) ncs = self.has_borders(self.next) for i, (child, pc, nc) in enumerate(zip(self.children, pcs, ncs)): result.append(_UnitBorder(char=char(i, pc, nc))) result.append(child) # Border right. result.append(_UnitBorder(char=char(self.columns))) # Padding right. if self.align in (HorizontalAlign.CENTER, HorizontalAlign.LEFT): result.append(Window(width=D(preferred=0))) return result
def process_steps(self): if self.intro: layout = Window( FormattedTextControl(to_formatted_text(HTML(self.intro))), wrap_lines=True, ) self.steps.append( { 'layout': layout, 'label': 'Introduction', 'handler': None, }, ) for handler in self.handlers: layout = handler.get_layout() layout.align = HorizontalAlign.JUSTIFY self.steps.append( { 'layout': layout, 'label': handler.get_label(), 'handler': handler, }, ) if self.summary: layout = Box( body=DynamicContainer(self.get_summary), padding=D(preferred=1, max=1), padding_bottom=1, ) self.steps.append( { 'layout': layout, 'label': 'Summary', 'handler': None, }, )
def __init__(self, title='', text='', ok_text='Ok', width=None, wrap_lines=True, scrollbar=False): self.future = Future() def accept_text(buf): get_app().layout.focus(ok_button) buf.complete_state = None return True def accept(): self.future.set_result(self.text_area.text) def cancel(): self.future.set_result(None) text_width = len(max(text.split('\n'), key=len)) + 2 self.text_area = TextArea(completer=completer, multiline=False, width=D(preferred=text_width), accept_handler=accept_text) ok_button = Button(text='OK', handler=accept) cancel_button = Button(text='Cancel', handler=cancel) self.dialog = Dialog(title=title, body=HSplit([Label(text=text), self.text_area]), buttons=[ok_button, cancel_button], width=width, modal=True)
def __init__(self, event, auth): super().__init__() self.event = event self.authtype = 'digest' authuser = auth.get('user', '') authpass = auth.get('password', '') self.authuser.text = authuser self.authuser.buffer.document = Document(authuser, len(authuser)) self.authpass_one.text = authpass self.authpass_one.buffer.document = Document(authpass, len(authpass)) self.authpass_two.text = authpass self.authpass_two.buffer.document = Document(authpass, len(authpass)) self.dialog = Dialog( title='Digest Authentication', body=HSplit([ Label(text='Username:\n'), self.authuser, Window(height=1, char=' '), Label(text='Password'), self.authpass_one, Window(height=1, char=' '), Label(text='Retype password'), self.authpass_two ]), buttons=[self.ok_button, self.cancel_button, self.delete_button], width=D(preferred=80), with_background=True, modal=True) root_container.floats.append(Float(self.dialog)) event.app.layout.focus(self.dialog)
def __init__(self, event, dialog): def ok_handler(): root_container.floats.pop() db.update_one(values={'name': name, 'auth': None}) event.app.layout.focus(ButtonManager.prev_button) select_item(event) def cancel_handler(): root_container.floats.pop() root_container.floats.append(self.auth_float) event.app.layout.focus(dialog) ok_button = Button(text='OK', handler=ok_handler) cancel_button = Button(text='Cancel', handler=cancel_handler) name = ButtonManager.current_button self.dialog = Dialog( title='Delete confirmation', body=Label( text='Are you sure you want to delete authentication for {}?'. format(name)), buttons=[cancel_button, ok_button], width=D(preferred=80), with_background=True) self.auth_float = root_container.floats.pop() root_container.floats.append(Float(self.dialog)) event.app.layout.focus(self.dialog)
def get_layout(self): widget = self.get_widget() vsplit_components = [widget] if 'message' in self._question and self._question['message']: vsplit_components.insert( 0, Label( self._question['message'], dont_extend_width=True, dont_extend_height=False, style='class:input.question', ), ) hsplit_components = [VSplit(vsplit_components, padding=1)] if 'description' in self._question and self._question['description']: hsplit_components.insert( 0, Window( FormattedTextControl( FormattedText([( 'class:input.question', self._question['description'], )]), ), wrap_lines=True, height=D(min=1, max=5, preferred=3), ), ) return HSplit(hsplit_components, padding=1)
def __init__(self, title, text, asking=False): self.future = Future() def set_done(): self.future.set_result(None) def accept(): self.future.set_result(True) def cancel(): self.future.set_result(False) if asking: buttons = [ Button(text="Yes", handler=accept), Button(text="No", handler=cancel), ] else: buttons = [Button(text="OK", handler=set_done)] text = "\n".join( [textwrap.fill(line, width=71) for line in text.splitlines()]) self.dialog = Dialog( title=title, body=HSplit([Label(text=text)]), buttons=buttons, width=D(preferred=75), modal=True, )
def __init__(self, event): def ok_handler(): # if len(ButtonManager.buttons) > 0: delete_server(event, name) root_container.floats.pop() def cancel_handler(): root_container.floats.pop() event.app.layout.focus(ButtonManager.prev_button) # Get data about server currently editing name = ButtonManager.current_button # Dialog configuration ok_button = Button(text='OK', handler=ok_handler) cancel_button = Button(text='Cancel', handler=cancel_handler) self.dialog = Dialog( title='Delete confirmation', body=Label( text='Are you sure you want to delete {}?'.format(name)), buttons=[cancel_button, ok_button], width=D(preferred=80), with_background=True) root_container.floats.append(Float(self.dialog)) event.app.layout.focus(self.dialog)
def __init__(self, title, text, yes_text, no_text, button=None): self.future = Future() def yes_handler(): self.future.set_result(True) def no_handler(): self.future.set_result(None) self.buttons = [ Button(text=yes_text, handler=yes_handler), Button(text=no_text, handler=no_handler), ] if button: self.buttons.insert( 1, Button(text=button[0], handler=lambda: button[1](self))) self.dialog = Dialog( title=title, body=HSplit([Label(text=text)]), buttons=self.buttons, width=D(preferred=50), modal=True, )
def create_content(self, progress_controls): return VSplit( progress_controls, height=lambda: D( preferred=len(self.models), max=len(self.models) ), )
def get_dimensions(cli): """ Return a list of LayoutDimension instances for this split. These dimensions will take the weight from the arrangement.VSplit/HSplit instances. """ average_weight = get_average_weight() # Make sure that weight is distributed result = [] for i, item in enumerate(split): result.append(D(weight=split.weights.get(item) or average_weight)) # Add dimension for the vertical border. last_item = i == len(split) - 1 if is_vsplit and not last_item: result.append(D.exact(1)) elif is_hsplit and not last_item: if pymux.enable_pane_status: result.append(D.exact(0)) else: result.append(D.exact(1)) return result
def __init__( self, text: AnyFormattedText, style: str = "", width: AnyDimension = None, dont_extend_height: bool = True, dont_extend_width: bool = False, ) -> None: self.text = text def get_width() -> AnyDimension: if width is None: text_fragments = to_formatted_text(self.text) text = fragment_list_to_text(text_fragments) if text: longest_line = max( get_cwidth(line) for line in text.splitlines()) else: return D(preferred=0) return D(preferred=longest_line) else: return width self.formatted_text_control = FormattedTextControl( text=lambda: self.text) self.window = Window( content=self.formatted_text_control, width=get_width, height=D(min=1), style="class:label " + style, dont_extend_height=dont_extend_height, dont_extend_width=dont_extend_width, )
def __init__(self, body, padding=None, padding_left=None, padding_right=None, padding_top=None, padding_bottom=None, width=None, height=None, style='', char=None, modal=False, key_bindings=None): assert is_container(body) if padding is None: padding = D(preferred=0) def get(value): if value is None: value = padding return to_dimension(value) self.padding_left = get(padding_left) self.padding_right = get(padding_right) self.padding_top = get(padding_top) self.padding_bottom = get(padding_bottom) self.body = body self.container = HSplit([ Window(height=self.padding_top, char=char), VSplit([ Window(width=self.padding_left, char=char), body, Window(width=self.padding_right, char=char), ]), Window(height=self.padding_bottom, char=char), ], width=width, height=height, style=style, modal=modal, key_bindings=None)
def make_app(sec, width, height, frame=True, gcalender=None, color=False): """make auto refresh application class""" kb = KeyBindings() @kb.add('c-c') def _(event): event.app.exit() def _vsplit(padding, m): return VSplit([Window(width=padding, always_hide_cursor=True), m, Window(width=padding, always_hide_cursor=True)]) if height == 14: clock = BigClock(sec=sec, color=color, gcalender=gcalender) elif height == 7: clock = MinimumClock(sec=sec, color=color, gcalender=gcalender) else: clock = SmallClock(sec=sec, color=color, gcalender=gcalender) padding = to_dimension(D(preferred=0)) body = Window(content=FormattedTextControl(text=clock.get_clock), width=width, height=height, always_hide_cursor=True) if frame: body = Frame(body) if gcalender is None: under_text = Window(height=padding, always_hide_cursor=True) else: ct = gcalender.get_calender_text_formatted if color else gcalender.get_calender_text under_text = Window(content=FormattedTextControl(text=ct), width=gcalender.get_max_length(), height=padding, always_hide_cursor=True) # make container app root_container = HSplit([Window(height=padding, always_hide_cursor=True), _vsplit(padding, body), _vsplit(padding, under_text)], key_bindings=None) layout = Layout(container=root_container) return Application(layout=layout, key_bindings=kb, full_screen=True, refresh_interval=.1)
def __init__(self, event=None, title='', text=''): content = Window(height=3, align=WindowAlign.CENTER, content=FormattedTextControl(text=text, show_cursor=False, modal=True)) body = HSplit([ Window(height=1, char=' '), content, Window(height=1, char=' '), ], padding=1) dialog = Dialog(body, title=title, width=D(preferred=80), with_background=True) loading_float = Float(content=dialog) root_container.floats.append(loading_float) self.focus = event.app.layout.focus self.focus(content)
def __enter__(self): # Create UI Application. title_toolbar = ConditionalContainer( Window(FormattedTextControl(lambda: self.title), height=1, style='class:progressbar,title'), filter=Condition(lambda: self.title is not None)) bottom_toolbar = ConditionalContainer( Window(FormattedTextControl(lambda: self.bottom_toolbar, style='class:bottom-toolbar.text'), style='class:bottom-toolbar', height=1), filter=~is_done & renderer_height_is_known & Condition(lambda: self.bottom_toolbar is not None)) def width_for_formatter(formatter): return formatter.get_width(progress_bar=self) progress_controls = [ Window(content=_ProgressControl(self, f), width=width_for_formatter(f)) for f in self.formatters ] self.app = Application( min_redraw_interval=.05, layout=Layout( HSplit([ title_toolbar, VSplit(progress_controls, height=lambda: D(preferred=len(self.counters), max=len(self.counters))), Window(), bottom_toolbar, ])), style=self.style, key_bindings=self.key_bindings, output=self.output, input=self.input) # Run application in different thread. def run(): with _auto_refresh_context(self.app, .3): try: self.app.run() except Exception as e: traceback.print_exc() print(e) self._thread = threading.Thread(target=run) self._thread.start() # Attach WINCH signal handler in main thread. # (Interrupt that we receive during resize events.) self._has_sigwinch = hasattr(signal, 'SIGWINCH') and in_main_thread() if self._has_sigwinch: self._previous_winch_handler = self._loop.add_signal_handler( signal.SIGWINCH, self.app.invalidate) return self
def input_dialog(title='', text='', ok_text='OK', cancel_text='Cancel', completer=None, password=False, style=None, async_=False): """ Display a text input box. Return the given text, or None when cancelled. """ def accept(buf): get_app().layout.focus(ok_button) return True # Keep text. def ok_handler(): get_app().exit(result=textfield.text) ok_button = Button(text=ok_text, handler=ok_handler) cancel_button = Button(text=cancel_text, handler=_return_none) textfield = TextArea( multiline=False, password=password, completer=completer, accept_handler=accept) dialog = Dialog( title=title, body=HSplit([ Label(text=text, dont_extend_height=True), textfield, ], padding=D(preferred=1, max=1)), buttons=[ok_button, cancel_button], with_background=True) return _run_dialog(dialog, style, async_=async_)
def __init__(self, event, title='', text=''): def ok_handler(): root_container.floats.pop() # If there was an original dialog, insert it back into layout if self.orig_dialog: root_container.floats.append(self.orig_dialog) event.app.layout.focus(root_container.float_container) else: event.app.layout.focus(ButtonManager.prev_button) ok_button = Button(text='OK', handler=ok_handler) dialog = Dialog(Window(wrap_lines=True, content=FormattedTextControl(text=text), always_hide_cursor=True), title=title, width=D(preferred=80), buttons=[ok_button], with_background=True) try: # If a dialog was already up, save it self.orig_dialog = root_container.floats.pop() except IndexError: self.orig_dialog = None root_container.floats.append(Float(content=dialog)) event.app.layout.focus(ok_button)
def progress_dialog( title: AnyFormattedText = "", text: AnyFormattedText = "", run_callback: Callable[[Callable[[int], None], Callable[[str], None]], None] = ( lambda *a: None ), style: Optional[BaseStyle] = None, ) -> Application[None]: """ :param run_callback: A function that receives as input a `set_percentage` function and it does the work. """ loop = get_event_loop() progressbar = ProgressBar() text_area = TextArea( focusable=False, # Prefer this text area as big as possible, to avoid having a window # that keeps resizing when we add text to it. height=D(preferred=10 ** 10), ) dialog = Dialog( body=HSplit( [ Box(Label(text=text)), Box(text_area, padding=D.exact(1)), progressbar, ] ), title=title, with_background=True, ) app = _create_app(dialog, style) def set_percentage(value: int) -> None: progressbar.percentage = int(value) app.invalidate() def log_text(text: str) -> None: loop.call_soon_threadsafe(text_area.buffer.insert_text, text) app.invalidate() # Run the callback in the executor. When done, set a return value for the # UI, so that it quits. def start() -> None: try: run_callback(set_percentage, log_text) finally: app.exit() def pre_run() -> None: run_in_executor_with_context(start) app.pre_run_callables.append(pre_run) return app