Add fast tap key sequence support and order-mode-type statuses

asyncify_input_modes
Tyler Goodlet 2021-06-17 16:52:54 -04:00
parent 3650db3321
commit 53074b552a
1 changed files with 94 additions and 32 deletions

View File

@ -19,7 +19,8 @@ Chart view box primitives
"""
from contextlib import asynccontextmanager
from typing import Optional
import time
from typing import Optional, Callable
import pyqtgraph as pg
from PyQt5.QtCore import Qt, QEvent
@ -31,6 +32,7 @@ import trio
from ..log import get_logger
from ._style import _min_points_to_show
from ._editors import SelectRect
from ._window import main_window
log = get_logger(__name__)
@ -43,16 +45,34 @@ async def handle_viewmode_inputs(
) -> None:
mode = view.mode
status_bar = main_window().status_bar
# track edge triggered keys
# (https://en.wikipedia.org/wiki/Interrupt#Triggering_methods)
pressed: set[str] = set()
last = time.time()
trigger_mode: str
action: str
# for quick key sequence-combo pattern matching
# we have a min_tap period and these should not
# ever be auto-repeats since we filter those at the
# event filter level prior to the above mem chan.
min_tap = 1/6
fast_key_seq: list[str] = []
fast_taps: dict[str, Callable] = {
'cc': mode.cancel_all_orders,
}
async for event, etype, key, mods, text in recv_chan:
log.debug(f'key: {key}, mods: {mods}, text: {text}')
now = time.time()
period = now - last
# reset mods
ctrl: bool = False
alt: bool = False
shift: bool = False
# press branch
@ -60,6 +80,27 @@ async def handle_viewmode_inputs(
pressed.add(key)
if (
# clear any old values not part of a "fast" tap sequence:
# presumes the period since last tap is longer then our
# min_tap period
fast_key_seq and period >= min_tap or
# don't support more then 2 key sequences for now
len(fast_key_seq) > 2
):
fast_key_seq.clear()
# capture key to fast tap sequence if we either
# have no previous keys or we do and the min_tap period is
# met
if (
not fast_key_seq or
period <= min_tap and fast_key_seq
):
fast_key_seq.append(text)
log.debug(f'fast keys seqs {fast_key_seq}')
# mods run through
if mods == Qt.ShiftModifier:
shift = True
@ -67,9 +108,7 @@ async def handle_viewmode_inputs(
if mods == Qt.ControlModifier:
ctrl = True
if QtCore.Qt.AltModifier == mods:
alt = True
# SEARCH MODE #
# ctlr-<space>/<l> for "lookup", "search" -> open search tree
if (
ctrl and key in {
@ -77,8 +116,7 @@ async def handle_viewmode_inputs(
Qt.Key_Space,
}
):
search = view._chart._lc.godwidget.search
search.focus()
view._chart._lc.godwidget.search.focus()
# esc and ctrl-c
if key == Qt.Key_Escape or (ctrl and key == Qt.Key_C):
@ -89,10 +127,7 @@ async def handle_viewmode_inputs(
# cancel order or clear graphics
if key == Qt.Key_C or key == Qt.Key_Delete:
# delete any lines under the cursor
mode = view.mode
for line in mode.lines.lines_under_cursor():
mode.book.cancel(uuid=line.oid)
mode.cancel_orders_under_cursor()
# View modes
if key == Qt.Key_R:
@ -100,54 +135,80 @@ async def handle_viewmode_inputs(
# edge triggered default view activation
view.chart.default_view()
if len(fast_key_seq) > 1:
# begin matches against sequences
func: Callable = fast_taps.get(''.join(fast_key_seq))
if func:
func()
fast_key_seq.clear()
# release branch
elif etype in {QEvent.KeyRelease}:
if key in pressed:
pressed.remove(key)
# selection mode
# SELECTION MODE #
if shift:
if view.state['mouseMode'] == ViewBox.PanMode:
view.setMouseMode(ViewBox.RectMode)
else:
# if view.state['mouseMode'] == ViewBox.RectMode:
view.setMouseMode(ViewBox.PanMode)
# order mode live vs. dark trigger
# ORDER MODE #
# live vs. dark trigger + an action {buy, sell, alert}
# 's' or ctrl to activate "live" submissions
if (
Qt.Key_S in pressed or
ctrl
):
view.mode._exec_mode = 'live'
else:
view.mode._exec_mode = 'dark'
order_keys_pressed = {Qt.Key_A, Qt.Key_F, Qt.Key_D}.intersection(pressed)
# order mode "action"
order_keys_pressed = {
Qt.Key_A,
Qt.Key_F,
Qt.Key_D
}.intersection(pressed)
if order_keys_pressed:
view._key_active = True
if (
# 's' for "submit" to activate "live" order
Qt.Key_S in pressed or
ctrl
):
trigger_mode: str = 'live'
else:
trigger_mode: str = 'dark'
# order mode trigger "actions"
if Qt.Key_D in pressed: # for "damp eet"
view.mode.set_exec('sell')
action = 'sell'
elif Qt.Key_F in pressed: # for "fillz eet"
view.mode.set_exec('buy')
action = 'buy'
elif Qt.Key_A in pressed:
view.mode.set_exec('alert')
action = 'alert'
trigger_mode = 'live'
view.order_mode = True
# XXX: order matters here for line style!
view.mode._exec_mode = trigger_mode
view.mode.set_exec(action)
prefix = trigger_mode + '-' if action != 'alert' else ''
view._chart.window().mode_label.setText(
f'mode: {prefix}{action}')
else: # none active
# if none are pressed, remove "staged" level
# line under cursor position
view.mode.lines.unstage_line()
view._key_active = False
if view.hasFocus():
# update mode label
view._chart.window().mode_label.setText('mode: view')
view.order_mode = False
last = time.time()
class ChartView(ViewBox):
@ -185,6 +246,7 @@ class ChartView(ViewBox):
self.name = name
self.mode = None
self.order_mode: bool = False
self.setFocusPolicy(QtCore.Qt.StrongFocus)
@ -402,7 +464,7 @@ class ChartView(ViewBox):
elif button == QtCore.Qt.LeftButton:
# when in order mode, submit execution
if self._key_active:
if self.order_mode:
ev.accept()
self.mode.submit_exec()