diff --git a/piker/ui/_display.py b/piker/ui/_display.py index 43085b7d..f7d78152 100644 --- a/piker/ui/_display.py +++ b/piker/ui/_display.py @@ -61,7 +61,10 @@ log = get_logger(__name__) _quote_throttle_rate: int = 58 # Hz -def try_read(array: np.ndarray) -> Optional[np.ndarray]: +def try_read( + array: np.ndarray + +) -> Optional[np.ndarray]: ''' Try to read the last row from a shared mem array or ``None`` if the array read returns a zero-length array result. @@ -85,10 +88,9 @@ def try_read(array: np.ndarray) -> Optional[np.ndarray]: # something we need anyway, maybe there should be some kind of # signal that a prepend is taking place and this consumer can # respond (eg. redrawing graphics) accordingly. - log.warning(f'Read-race on shm array: {graphics_name}@{shm.token}') - # the array read was emtpy - return None + # the array read was emtpy + return None def update_fsp_chart( @@ -101,8 +103,10 @@ def update_fsp_chart( array = shm.array last_row = try_read(array) + # guard against unreadable case if not last_row: + log.warning(f'Read-race on shm array: {graphics_name}@{shm.token}') return # update graphics @@ -175,7 +179,6 @@ def chart_maxmin( async def update_chart_from_quotes( - linked: LinkedSplits, stream: tractor.MsgStream, ohlcv: np.ndarray, @@ -247,17 +250,16 @@ async def update_chart_from_quotes( chart.show() last_quote = time.time() - # NOTE: all code below this loop is expected to be synchronous - # and thus draw instructions are not picked up jntil the next - # wait / iteration. async for quotes in stream: now = time.time() - quote_period = now - last_quote - quote_rate = round(1/quote_period, 1) if quote_period else float('inf') + quote_period = time.time() - last_quote + quote_rate = round( + 1/quote_period, 1) if quote_period > 0 else float('inf') + if ( quote_period <= 1/_quote_throttle_rate - and quote_rate > _quote_throttle_rate + 2 + and quote_rate > _quote_throttle_rate * 1.5 ): log.warning(f'High quote rate {symbol.key}: {quote_rate}') last_quote = now @@ -454,7 +456,8 @@ def maybe_mk_fsp_shm( readonly: bool = True, ) -> (ShmArray, bool): - '''Allocate a single row shm array for an symbol-fsp pair if none + ''' + Allocate a single row shm array for an symbol-fsp pair if none exists, otherwise load the shm already existing for that token. ''' @@ -481,7 +484,6 @@ def maybe_mk_fsp_shm( @acm async def open_fsp_sidepane( - linked: LinkedSplits, conf: dict[str, dict[str, str]], @@ -570,6 +572,7 @@ async def open_fsp_cluster( async def maybe_open_fsp_cluster( workers: int = 2, **kwargs, + ) -> AsyncGenerator[int, dict[str, tractor.Portal]]: kwargs.update( @@ -589,7 +592,6 @@ async def maybe_open_fsp_cluster( async def start_fsp_displays( - cluster_map: dict[str, tractor.Portal], linkedsplits: LinkedSplits, fsps: dict[str, str], @@ -603,7 +605,8 @@ async def start_fsp_displays( display_in_own_task: bool = False, ) -> None: - '''Create sub-actors (under flat tree) + ''' + Create sub-actors (under flat tree) for each entry in config and attach to local graphics update tasks. Pass target entrypoint and historical data. @@ -668,9 +671,7 @@ async def start_fsp_displays( async def update_chart_from_fsp( - portal: tractor.Portal, - linkedsplits: LinkedSplits, brokermod: ModuleType, sym: str, @@ -687,7 +688,8 @@ async def update_chart_from_fsp( profiler: pg.debug.Profiler, ) -> None: - '''FSP stream chart update loop. + ''' + FSP stream chart update loop. This is called once for each entry in the fsp config map. @@ -792,9 +794,7 @@ async def update_chart_from_fsp( level_line(chart, 80, orient_v='top') chart._set_yrange() - - done() - chart.linked.resize_sidepanes() + done() # status updates profiler(f'fsp:{func_name} starting update loop') profiler.finish() @@ -912,7 +912,6 @@ def has_vlm(ohlcv: ShmArray) -> bool: @acm async def maybe_open_vlm_display( - linked: LinkedSplits, ohlcv: ShmArray, @@ -992,20 +991,10 @@ async def maybe_open_vlm_display( # size view to data once at outset chart._set_yrange() - # size pain to parent chart - # TODO: this appears to nearly fix a bug where the vlm sidepane - # could be sized correctly nearly immediately (since the - # order pane is already sized), right now it doesn't seem to - # fully align until the VWAP fsp-actor comes up... - await trio.sleep(0) - chart.linked.resize_sidepanes() - await trio.sleep(0) - yield chart async def display_symbol_data( - godwidget: GodWidget, provider: str, sym: str, @@ -1116,24 +1105,24 @@ async def display_symbol_data( 'chart_kwargs': {'style': 'step'} }, - 'rsi': { - 'func_name': 'rsi', # literal python func ref lookup name + # 'rsi': { + # 'func_name': 'rsi', # literal python func ref lookup name - # map of parameters to place on the fsp sidepane widget - # which should map to dynamic inputs available to the - # fsp function at runtime. - 'params': { - 'period': { - 'default_value': 14, - 'widget_kwargs': {'readonly': True}, - }, - }, + # # map of parameters to place on the fsp sidepane widget + # # which should map to dynamic inputs available to the + # # fsp function at runtime. + # 'params': { + # 'period': { + # 'default_value': 14, + # 'widget_kwargs': {'readonly': True}, + # }, + # }, - # ``ChartPlotWidget`` options passthrough - 'chart_kwargs': { - 'static_yrange': (0, 100), - }, - }, + # # ``ChartPlotWidget`` options passthrough + # 'chart_kwargs': { + # 'static_yrange': (0, 100), + # }, + # }, } if has_vlm(ohlcv): # and provider != 'binance': @@ -1201,4 +1190,10 @@ async def display_symbol_data( order_mode_started ) ): + # let Qt run to render all widgets and make sure the + # sidepanes line up vertically. + await trio.sleep(0) + linkedsplits.resize_sidepanes() + + # let the app run. await trio.sleep_forever()