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 contextlib import asynccontextmanager
from typing import Optional import time
from typing import Optional, Callable
import pyqtgraph as pg import pyqtgraph as pg
from PyQt5.QtCore import Qt, QEvent from PyQt5.QtCore import Qt, QEvent
@ -31,6 +32,7 @@ import trio
from ..log import get_logger from ..log import get_logger
from ._style import _min_points_to_show from ._style import _min_points_to_show
from ._editors import SelectRect from ._editors import SelectRect
from ._window import main_window
log = get_logger(__name__) log = get_logger(__name__)
@ -43,16 +45,34 @@ async def handle_viewmode_inputs(
) -> None: ) -> None:
mode = view.mode
status_bar = main_window().status_bar
# track edge triggered keys # track edge triggered keys
# (https://en.wikipedia.org/wiki/Interrupt#Triggering_methods) # (https://en.wikipedia.org/wiki/Interrupt#Triggering_methods)
pressed: set[str] = set() 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: async for event, etype, key, mods, text in recv_chan:
log.debug(f'key: {key}, mods: {mods}, text: {text}') log.debug(f'key: {key}, mods: {mods}, text: {text}')
now = time.time()
period = now - last
# reset mods # reset mods
ctrl: bool = False ctrl: bool = False
alt: bool = False
shift: bool = False shift: bool = False
# press branch # press branch
@ -60,6 +80,27 @@ async def handle_viewmode_inputs(
pressed.add(key) 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 # mods run through
if mods == Qt.ShiftModifier: if mods == Qt.ShiftModifier:
shift = True shift = True
@ -67,9 +108,7 @@ async def handle_viewmode_inputs(
if mods == Qt.ControlModifier: if mods == Qt.ControlModifier:
ctrl = True ctrl = True
if QtCore.Qt.AltModifier == mods: # SEARCH MODE #
alt = True
# ctlr-<space>/<l> for "lookup", "search" -> open search tree # ctlr-<space>/<l> for "lookup", "search" -> open search tree
if ( if (
ctrl and key in { ctrl and key in {
@ -77,8 +116,7 @@ async def handle_viewmode_inputs(
Qt.Key_Space, Qt.Key_Space,
} }
): ):
search = view._chart._lc.godwidget.search view._chart._lc.godwidget.search.focus()
search.focus()
# esc and ctrl-c # esc and ctrl-c
if key == Qt.Key_Escape or (ctrl and key == Qt.Key_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 # cancel order or clear graphics
if key == Qt.Key_C or key == Qt.Key_Delete: if key == Qt.Key_C or key == Qt.Key_Delete:
# delete any lines under the cursor mode.cancel_orders_under_cursor()
mode = view.mode
for line in mode.lines.lines_under_cursor():
mode.book.cancel(uuid=line.oid)
# View modes # View modes
if key == Qt.Key_R: if key == Qt.Key_R:
@ -100,54 +135,80 @@ async def handle_viewmode_inputs(
# edge triggered default view activation # edge triggered default view activation
view.chart.default_view() 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 # release branch
elif etype in {QEvent.KeyRelease}: elif etype in {QEvent.KeyRelease}:
if key in pressed: if key in pressed:
pressed.remove(key) pressed.remove(key)
# selection mode # SELECTION MODE #
if shift: if shift:
if view.state['mouseMode'] == ViewBox.PanMode: if view.state['mouseMode'] == ViewBox.PanMode:
view.setMouseMode(ViewBox.RectMode) view.setMouseMode(ViewBox.RectMode)
else: else:
# if view.state['mouseMode'] == ViewBox.RectMode:
view.setMouseMode(ViewBox.PanMode) 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 order_keys_pressed = {
if ( Qt.Key_A,
Qt.Key_S in pressed or Qt.Key_F,
ctrl Qt.Key_D
): }.intersection(pressed)
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"
if order_keys_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" # order mode trigger "actions"
if Qt.Key_D in pressed: # for "damp eet" 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" elif Qt.Key_F in pressed: # for "fillz eet"
view.mode.set_exec('buy') action = 'buy'
elif Qt.Key_A in pressed: 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 else: # none active
# if none are pressed, remove "staged" level # if none are pressed, remove "staged" level
# line under cursor position # line under cursor position
view.mode.lines.unstage_line() 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): class ChartView(ViewBox):
@ -185,6 +246,7 @@ class ChartView(ViewBox):
self.name = name self.name = name
self.mode = None self.mode = None
self.order_mode: bool = False
self.setFocusPolicy(QtCore.Qt.StrongFocus) self.setFocusPolicy(QtCore.Qt.StrongFocus)
@ -402,7 +464,7 @@ class ChartView(ViewBox):
elif button == QtCore.Qt.LeftButton: elif button == QtCore.Qt.LeftButton:
# when in order mode, submit execution # when in order mode, submit execution
if self._key_active: if self.order_mode:
ev.accept() ev.accept()
self.mode.submit_exec() self.mode.submit_exec()