Key binding šļøĀ¶
A key binding is an association between a physical key on a keyboard and a parameter. A parameter can have any number of key bindings associated with it, and a particular key binding can control any number of parameters.
Note
This page contains a couple of extra notes about key bindings.
Key bindings can be defined by importing quo.keys.bind()
which is an instance of Bind
from quo.keys import bind
@bind.add('a')
def _(event):
" Do something if 'a' has been pressed. "
...
@bind.add('ctrl-t')
def _(event):
" Do something if Control-T has been pressed. "
...
Note
ctrl-q (control-q) and ctrl-s (control-s) are often captured by the terminal, because they were used traditionally for software flow control. When this is enabled, the application will automatically freeze when ctrl-s is pressed, until ctrl-q is pressed. It wonāt be possible to bind these keys.
In order to disable this, execute the following command in your shell, or even add it to your .bashrc.
stty -ixon
Key bindings can even consist of a sequence of multiple keys. The binding is only triggered when all the keys in this sequence are pressed.
@bind.add('q', 'u', 'o')
def _(start):
" Do something if 'q' is pressed, then 'u' and then 'o' is pressed. "
...
If the user presses only q, then nothing will happen until either a second key (like u or o) has been pressed or until the timeout expires.
List of special keys
¶
Besides literal characters, any of the following keys can be used in a key binding:
Name |
Possible keys |
---|---|
Escape Shift + escape |
escape s-escape |
Arrows |
left, right, up, down |
Navigation |
home, end, delete, pageup, pagedown, insert |
Control+letter |
ctrl-a, ctrl-b, ctrl-c, ctrl-d, ctrl-e, ctrl-f, ctrl-g, ctrl-h, cttl-i, ctrl-j, ctrl-k, ctrl-l, ctrl-m, ctrl-n, ctrl-o, ctrl-p, ctrl-q, ctrl-r, ctrl-s, ctrl-t, ctrl-u, ctrl-v, ctrl-w, ctrl-x, ctrl-y, ctrl-z |
Control + number |
ctrl-1, ctrl-2, ctrl-3, ctrl-4, ctrl-5, ctrl-6, ctrl-7, ctrl-8, ctrl-9, ctrl-0 |
Control + arrow |
ctrl-left, ctrl-right, ctrl-up, ctrl-down |
Other control keys |
ctrl-@, ctrl-\, ctrl-], ctrl-^, ctrl-_, ctrl-delete |
Shift + arrow |
s-left, s-right, s-up, s-down |
Control + Shift + arrow |
c-s-left, c-s-right, c-s-up, c-s-down |
Other shift keys |
s-delete, s-tab |
F-keys |
f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, f21, f22, f23, f24 |
There are a couple of useful aliases as well:
ctrl-h | backspace |
||
ctrl-@ | ctrl-space |
||
ctrl-m | enter |
||
ctrl-i | tab |
Note
Note that the supported keys are limited to what typical VT100 terminals offer. Binding ctrl-7 (control + number 7) for instance is not supported.
Binding alt+something, option+something or meta+something
¶
Vt100 terminals translate the alt key into a leading escape key. For instance, in order to handle alt-f, we have to handle escape + f. Notice that we receive this as two individual keys. This means that itās exactly the same as first typing escape and then typing f. Something this alt-key is also known as option or meta.
In code that looks as follows:
@bind.add('escape', 'f')
def _(event):
" Do something if alt-f or meta-f have been pressed. "
Wildcards
¶
Sometimes you want to catch any key that follows after a certain key stroke. This is possible by binding the ā<any>ā key:
@bind.add('a', '<any>')
def _(start):
...
This will handle aa, ab, ac, etcetera. The key binding can check the event object for which keys exactly have been pressed.
Attaching a Condition to key bindings
¶
In order to enable a key binding according to a certain condition, we have to
pass it to Condition
instance. (Read more about filters.)
import datetime
from quo.filters import Condition
from quo.keys import bind
@Condition
def is_active():
" Only activate key binding on the second half of each minute. "
return datetime.datetime.now().second > 30
@bind.add('ctrl-t', filter=is_active)
def _(event):
# ...
pass
The key binding will be ignored when this condition is not satisfied.
ConditionalKeyBindings: Disabling a set of key bindings
¶
Sometimes you want to enable or disable a whole set of key bindings according to a certain condition. This is possible by wrapping it in a
ConditionalKeyBindings
object.
from quo.filters import Condition
from quo.keys ConditionalKeyBindings
@Condition
def is_active():
" Only activate key binding on the second half of each minute. "
return datetime.datetime.now().second > 30
bindings = ConditionalKeyBindings(
bind=my_bindings,
filter=is_active)
If the condition is not satisfied, all the key bindings in my_bindings above will be ignored.
Merging key bindings
¶
Sometimes you have different parts of your application generate a collection of
key bindings. It is possible to merge them together through the
merge_key_bindings()
function. This is preferred above passing a Bind
object around and having everyone populate it.
from quo.keys import merge_key_bindings
bindings = merge_key_bindings([
bindings1,
bindings2,
])
Eager
¶
Usually not required, but if ever you have to override an existing key binding, the eager flag can be useful.
Suppose that there is already an active binding for ab and youād like to add a second binding that only handles a. When the user presses only a, quo has to wait for the next key press in order to know which handler to call.
By passing the eager flag to this second binding, we are actually saying that quo shouldnāt wait for longer matches when all the keys in this key binding are matched. So, if a has been pressed, this second binding will be called, even if thereās an active ab binding.
@bind.add('a', 'b')
def binding_1(event):
...
@bind.add('a', eager=True)
def binding_2(event):
...
This is mainly useful in order to conditionally override another binding.
Asyncio coroutines
¶
Key binders handlers can be asyncio coroutines.
@bind.add('x')
async def print_hello(event):
"""
Pressing 'x' will print 5 times "hello" in the background above the
prompt.
"""
for i in range(5):
# Print hello above the current prompt.
print("Hello")
# Sleep, but allow further input editing in the meantime.
await asyncio.sleep(1)
If the user accepts the input on the prompt, while this coroutine is not yet finished , an asyncio.CancelledError exception will be thrown in this coroutine.
Timeouts
¶
There are two timeout settings that effect the handling of keys.
Application.ttimeoutlen
: Like Vimās ttimeoutlen option. When to flush the input (For flushing escape keys.) This is important on terminals that use vt100 input. We canāt distinguish the escape key from for instance the left-arrow key, if we donāt know what follows after āx1bā. This little timer will consider āx1bā to be escape if nothing did follow in this time span. This seems to work like the ttimeoutlen option in Vim.KeyProcessor.timeoutlen
: like Vimās timeoutlen option. This can be None or a float. For instance, suppose that we have a key binding AB and a second key binding A. If the uses presses A and then waits, we donāt handle this binding yet (unless it was marked āeagerā), because we donāt know what will follow. This timeout is the maximum amount of time that we wait until we call the handlers anyway. Pass None to disable this timeout.
Recording macros
¶
Both Emacs and Vi mode allow macro recording. By default, all key presses are recorded during a macro, but it is possible to exclude certain keys by setting the record_in_macro parameter to False:
@bind.add('ctrl-t', record_in_macro=False)
def _(event):
# ...
pass
Creating new Vi text objects and operators
¶
We tried very hard to ship prompt_toolkit with as many as possible Vi text objects and operators, so that text editing feels as natural as possible to Vi users.
If you wish to create a new text object or key binding, that is actually possible. Check the custom-vi-operator-and-text-object.py example for more information.