TIL

Today I Learned. 知ったこと、学んだことを書いていく

Bufferのread_onlyを一時的にOFFにしたいとき - Python

Bufferのread_onlyAlways()だと、Bufferのtextとかdocumentとか何も変更できないから、Never()にする必要がある

form prompt_toolkit.filter import to_filter

# read_onlyをONにする
right.content.buffer.read_only = to_filter(True)

# read_onlyをOFFにする
right.content.buffer.read_only = to_filter(False)

使用例

from prompt_toolkit.application import Application
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.layout.containers import VSplit, Window
from prompt_toolkit.layout.controls import BufferControl
from prompt_toolkit.layout.layout import Layout
from prompt_toolkit.buffer import Buffer
from prompt_toolkit.document import Document
from prompt_toolkit.filters import to_filter


text = '\n'.join([str(i) for i in range(100)])

left = Window(BufferControl(Buffer(document=Document(text), read_only=True)))
right = Window(BufferControl(Buffer(document=Document(text), read_only=True)))

# ウィンドウ
body = VSplit([
    left,
    Window(width=1, char='|'),
    right,
    ])


# キーバインディング
kb = KeyBindings()


@kb.add('tab')
def _(event):
    event.app.layout.focus_next()


@kb.add('s-tab')
def _(event):
    event.app.layout.focus_previous()


@kb.add('q')
def _(event):
    event.app.exit()


@kb.add('c-o')
def _(event):
    ## ここで、一時的にread_onlyをOFFにしている
    right.content.buffer.read_only = to_filter(False)
    right.content.buffer.document = Document('hello world!')
    right.content.buffer.read_only = to_filter(True)


app = Application(
        layout=Layout(body),
        key_bindings=kb,
        full_screen=True)

app.run()



以下、いろいろ調べたときのメモ

documentを変えるときだけ、read_onlyをFalseにする

Documnetのコンストラクタを見ると引数でもらったread_onluto_filter()に渡している

https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/buffer.py#L196

class Buffer(object):
    ...
    def __init__(self, completer=None, auto_suggest=None, history=None,
                 validator=None, tempfile_suffix='', name='',
                 complete_while_typing=False, validate_while_typing=False,
                 enable_history_search=False, document=None,
                 accept_handler=None, read_only=False, multiline=True,
                 on_text_changed=None, on_text_insert=None,
                 on_cursor_position_changed=None, on_completions_changed=None,
                 on_suggestion_set=None):

        # Accept both filters and booleans as input.
        enable_history_search = to_filter(enable_history_search)
        complete_while_typing = to_filter(complete_while_typing)
        validate_while_typing = to_filter(validate_while_typing)
        read_only = to_filter(read_only)
        ...

to_filter()ってなに

prompt_toolkit.filters.utilsにいた

https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/filters/utils.py

Trueを渡すと、prompt_toolkit.filters.base.Always()が返される。
Falseを渡すと、prompt_toolkit.filters.base.Never()が返される。

from __future__ import unicode_literals
from .base import Always, Never, Filter

__all__ = [
    'to_filter',
    'is_true',
]

_always = Always()
_never = Never()


def to_filter(bool_or_filter):
    """
    Accept both booleans and Filters as input and
    turn it into a Filter.
    """
    if not isinstance(bool_or_filter, (bool, Filter)):
        raise TypeError('Expecting a bool or a Filter instance. Got %r' % bool_or_filter)

    return {
        True: _always,
        False: _never,
    }.get(bool_or_filter, bool_or_filter)


def is_true(value):
    """
    Test whether `value` is True. In case of a Filter, call it.

    :param value: Boolean or `Filter` instance.
    """
    return to_filter(value)()

AlwaysとNeverってなに

prompt_toolkit.filters.baseにいた

https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/filters/base.py#L173-#L192

class Always(Filter):
    """
    Always enable feature.
    """
    def __call__(self):
        return True

    def __invert__(self):
        return Never()


class Never(Filter):
    """
    Never enable feature.
    """
    def __call__(self):
        return False

    def __invert__(self):
        return Always()

__call____invert__って何

__call__インスタンス生成以外で関数っぽく呼び出した時に呼ばれる

Pythonのクラスにおけるcallメソッドの使い方 - Qiita

__invert__は反転って意味らしい

よくわからなくなってきたけど、こうすればいいってことだ

form prompt_toolkit.filter import to_filter

# read_onlyにするとき
right.content.buffer.read_only = to_filter(True)

# read_onlyを解除するとき
right.content.buffer.read_only = to_filter(False)