From 3db0cf9054e3f5152ddfa6f7ad5dc0c6444b0f00 Mon Sep 17 00:00:00 2001 From: goodboy Date: Wed, 4 Feb 2026 18:52:47 -0500 Subject: [PATCH] Add order-cancel debugging and multiline kbd logs Add verbose logging + error handling for order cancellation hotkey path and multiline style for view-mode kb msgs. Deats, - add `Cursor.is_hovered()` to check hover state - log warnings when no orders cancelled via hotkey - add try-except around `.cancel_orders_under_cursor()` - log `cur._hovered` state in `.ui._lines` hover handlers - change `Dialog.cancel_orders()` to return `list[Dialog]` - fix import: `Flume` from `.data.flows` vs `.data.feed` - comment-out multi-status msgs in order submit/cancel Also, - convert all multiline kbd `if` conditionals to use `and` on separate lines for consistency - move `import tractor` to top of `._interaction` - change `print()` to `log.debug()` in `LevelLine` - fix type annotation spacing: `Callable|None` vs `Callable | None` (this commit msg was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- piker/ui/_cursor.py | 11 ++++++- piker/ui/_dataviz.py | 2 +- piker/ui/_interaction.py | 67 +++++++++++++++++++++++++++++++--------- piker/ui/_lines.py | 20 ++++++++++-- piker/ui/order_mode.py | 34 ++++++++++---------- 5 files changed, 98 insertions(+), 36 deletions(-) diff --git a/piker/ui/_cursor.py b/piker/ui/_cursor.py index 7675b2e0..393b58ef 100644 --- a/piker/ui/_cursor.py +++ b/piker/ui/_cursor.py @@ -413,9 +413,18 @@ class Cursor(pg.GraphicsObject): self, item: pg.GraphicsObject, ) -> None: - assert getattr(item, 'delete'), f"{item} must define a ``.delete()``" + assert getattr( + item, + 'delete', + ), f"{item} must define a ``.delete()``" self._hovered.add(item) + def is_hovered( + self, + item: pg.GraphicsObject, + ) -> bool: + return item in self._hovered + def add_plot( self, plot: ChartPlotWidget, # noqa diff --git a/piker/ui/_dataviz.py b/piker/ui/_dataviz.py index 36251e48..cc4529be 100644 --- a/piker/ui/_dataviz.py +++ b/piker/ui/_dataviz.py @@ -45,7 +45,7 @@ from piker.ui.qt import QLineF from ..data._sharedmem import ( ShmArray, ) -from ..data.feed import Flume +from ..data.flows import Flume from ..data._formatters import ( IncrementalFormatter, OHLCBarsFmtr, # Plain OHLC renderer diff --git a/piker/ui/_interaction.py b/piker/ui/_interaction.py index 9bd48139..2e3107ac 100644 --- a/piker/ui/_interaction.py +++ b/piker/ui/_interaction.py @@ -43,6 +43,7 @@ from pyqtgraph import ( functions as fn, ) import numpy as np +import tractor import trio from piker.ui.qt import ( @@ -72,7 +73,10 @@ if TYPE_CHECKING: GodWidget, ) from ._dataviz import Viz - from .order_mode import OrderMode + from .order_mode import ( + OrderMode, + Dialog, + ) from ._display import DisplayState @@ -130,7 +134,12 @@ async def handle_viewmode_kb_inputs( async for kbmsg in recv_chan: event, etype, key, mods, text = kbmsg.to_tuple() - log.debug(f'key: {key}, mods: {mods}, text: {text}') + log.debug( + f'View-mode kb-msg received,\n' + f'mods: {mods!r}\n' + f'key: {key!r}\n' + f'text: {text!r}\n' + ) now = time.time() period = now - last @@ -158,8 +167,12 @@ async def handle_viewmode_kb_inputs( # 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 + 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}') @@ -174,7 +187,8 @@ async def handle_viewmode_kb_inputs( # UI REPL-shell, with ctrl-p (for "pause") if ( ctrl - and key in { + and + key in { Qt.Key_P, } ): @@ -184,7 +198,6 @@ async def handle_viewmode_kb_inputs( vlm_chart = chart.linked.subplots['volume'] # noqa vlm_viz = vlm_chart.main_viz # noqa dvlm_pi = vlm_chart._vizs['dolla_vlm'].plot # noqa - import tractor await tractor.pause() view.interact_graphics_cycle() @@ -192,7 +205,8 @@ async def handle_viewmode_kb_inputs( # shown data `Viz`s for the current chart app. if ( ctrl - and key in { + and + key in { Qt.Key_R, } ): @@ -231,7 +245,8 @@ async def handle_viewmode_kb_inputs( key == Qt.Key_Escape or ( ctrl - and key == Qt.Key_C + and + key == Qt.Key_C ) ): # ctrl-c as cancel @@ -242,17 +257,35 @@ async def handle_viewmode_kb_inputs( # cancel order or clear graphics if ( key == Qt.Key_C - or key == Qt.Key_Delete + or + key == Qt.Key_Delete ): + # log.info('Handling hotkey!') + try: + dialogs: list[Dialog] = order_mode.cancel_orders_under_cursor() + except BaseException: + log.exception('Failed to cancel orders !?\n') + await tractor.pause() - order_mode.cancel_orders_under_cursor() + if not dialogs: + log.warning( + 'No orders were cancelled?\n' + 'Is there an order-line under the cursor?\n' + 'If you think there IS your DE might be "hiding the mouse" before ' + 'we rx the keyboard input via Qt..\n' + '=> Check your DE and/or TWM settings to be sure! <=\n' + ) + # ^TODO?, some way to detect if there's lines and + # the DE is cuckin with things? + # await tractor.pause() # View modes if ( ctrl and ( key == Qt.Key_Equal - or key == Qt.Key_I + or + key == Qt.Key_I ) ): view.wheelEvent( @@ -264,7 +297,8 @@ async def handle_viewmode_kb_inputs( ctrl and ( key == Qt.Key_Minus - or key == Qt.Key_O + or + key == Qt.Key_O ) ): view.wheelEvent( @@ -275,7 +309,8 @@ async def handle_viewmode_kb_inputs( elif ( not ctrl - and key == Qt.Key_R + and + key == Qt.Key_R ): # NOTE: seems that if we don't yield a Qt render # cycle then the m4 downsampled curves will show here @@ -477,7 +512,8 @@ async def handle_viewmode_mouse( # view.raiseContextMenu(event) if ( - view.order_mode.active and + view.order_mode.active + and button == QtCore.Qt.LeftButton ): # when in order mode, submit execution @@ -781,7 +817,8 @@ class ChartView(ViewBox): # Scale or translate based on mouse button if btn & ( - QtCore.Qt.LeftButton | QtCore.Qt.MidButton + QtCore.Qt.LeftButton + | QtCore.Qt.MidButton ): # zoom y-axis ONLY when click-n-drag on it # if axis == 1: diff --git a/piker/ui/_lines.py b/piker/ui/_lines.py index 61049fa7..3c9b91d5 100644 --- a/piker/ui/_lines.py +++ b/piker/ui/_lines.py @@ -51,10 +51,13 @@ from ._anchors import ( from ..calc import humanize from ._label import Label from ._style import hcolor, _font +from ..log import get_logger if TYPE_CHECKING: from ._cursor import Cursor +log = get_logger(__name__) + # TODO: probably worth investigating if we can # make .boundingRect() faster: @@ -346,7 +349,7 @@ class LevelLine(pg.InfiniteLine): ) -> None: # TODO: enter labels edit mode - print(f'double click {ev}') + log.debug(f'double click {ev}') def paint( self, @@ -460,10 +463,19 @@ class LevelLine(pg.InfiniteLine): # hovered if ( not ev.isExit() - and ev.acceptDrags(QtCore.Qt.LeftButton) + and + ev.acceptDrags(QtCore.Qt.LeftButton) ): # if already hovered we don't need to run again - if self.mouseHovering is True: + if ( + self.mouseHovering is True + and + cur.is_hovered(self) + ): + log.debug( + f'Already hovering ??\n' + f'cur._hovered: {cur._hovered!r}\n' + ) return if self.only_show_markers_on_hover: @@ -480,6 +492,7 @@ class LevelLine(pg.InfiniteLine): cur._y_label_update = False # add us to cursor state + log.debug(f'Adding line {self!r}\n') cur.add_hovered(self) if self._hide_xhair_on_hover: @@ -507,6 +520,7 @@ class LevelLine(pg.InfiniteLine): self.currentPen = self.pen + log.debug(f'Removing line {self!r}\n') cur._hovered.remove(self) if self.only_show_markers_on_hover: diff --git a/piker/ui/order_mode.py b/piker/ui/order_mode.py index 76bee0ef..3528ae64 100644 --- a/piker/ui/order_mode.py +++ b/piker/ui/order_mode.py @@ -77,7 +77,6 @@ from ._style import _font from ._forms import open_form_input_handling from ._notify import notify_from_ems_status_msg - if TYPE_CHECKING: from ._chart import ( ChartPlotWidget, @@ -436,7 +435,7 @@ class OrderMode: lines=lines, last_status_close=self.multistatus.open_status( f'submitting {order.exec_mode}-{order.action}', - final_msg=f'submitted {order.exec_mode}-{order.action}', + # final_msg=f'submitted {order.exec_mode}-{order.action}', clear_on_next=True, ) ) @@ -528,7 +527,7 @@ class OrderMode: # a submission is the start of a new order dialog dialog = self.dialogs[uuid] dialog.lines = lines - cls: Callable | None = dialog.last_status_close + cls: Callable|None = dialog.last_status_close if cls: cls() @@ -658,7 +657,7 @@ class OrderMode: return True - def cancel_orders_under_cursor(self) -> list[str]: + def cancel_orders_under_cursor(self) -> list[Dialog]: return self.cancel_orders( self.oids_from_lines( self.lines.lines_under_cursor() @@ -687,24 +686,28 @@ class OrderMode: self, oids: list[str], - ) -> None: + ) -> list[Dialog]: ''' Cancel all orders from a list of order ids: `oids`. ''' - key = self.multistatus.open_status( - f'cancelling {len(oids)} orders', - final_msg=f'cancelled orders:\n{oids}', - group_key=True - ) + # key = self.multistatus.open_status( + # f'cancelling {len(oids)} orders', + # final_msg=f'cancelled orders:\n{oids}', + # group_key=True + # ) + dialogs: list[Dialog] = [] for oid in oids: if dialog := self.dialogs.get(oid): self.client.cancel_nowait(uuid=oid) - cancel_status_close = self.multistatus.open_status( - f'cancelling order {oid}', - group_key=key, - ) - dialog.last_status_close = cancel_status_close + # cancel_status_close = self.multistatus.open_status( + # f'cancelling order {oid}', + # group_key=key, + # ) + # dialog.last_status_close = cancel_status_close + dialogs.append(dialog) + + return dialogs def cancel_all_orders(self) -> None: ''' @@ -776,7 +779,6 @@ class OrderMode: @asynccontextmanager async def open_order_mode( - feed: Feed, godw: GodWidget, fqme: str,