Compare commits

..

No commits in common. "c210c286a581574bef5d8431d151be56ccbffd46" and "c5fa2624746a92bd763a4cfe93f329920bb4481e" have entirely different histories.

2 changed files with 58 additions and 125 deletions

View File

@ -168,7 +168,7 @@ async def _reconnect_forever(
nobsws: NoBsWs, nobsws: NoBsWs,
reset_after: int, # msg recv timeout before reset attempt reset_after: int, # msg recv timeout before reset attempt
fixture: AsyncContextManager|None = None, fixture: AsyncContextManager | None = None,
task_status: TaskStatus = trio.TASK_STATUS_IGNORED, task_status: TaskStatus = trio.TASK_STATUS_IGNORED,
) -> None: ) -> None:
@ -185,7 +185,7 @@ async def _reconnect_forever(
async def proxy_msgs( async def proxy_msgs(
ws: WebSocketConnection, ws: WebSocketConnection,
rent_cs: trio.CancelScope, # parent cancel scope rent_cs: trio.CancelScope, # parent cancel scope
) -> None: ):
''' '''
Receive (under `timeout` deadline) all msgs from from underlying Receive (under `timeout` deadline) all msgs from from underlying
websocket and relay them to (calling) parent task via ``trio`` websocket and relay them to (calling) parent task via ``trio``
@ -206,7 +206,7 @@ async def _reconnect_forever(
except nobsws.recon_errors: except nobsws.recon_errors:
log.exception( log.exception(
f'{src_mod}\n' f'{src_mod}\n'
f'{url!r} connection failed\n' f'{url} connection bail with:'
) )
with trio.CancelScope(shield=True): with trio.CancelScope(shield=True):
await trio.sleep(0.5) await trio.sleep(0.5)
@ -269,7 +269,7 @@ async def _reconnect_forever(
nobsws._ws = ws nobsws._ws = ws
log.info( log.info(
f'{src_mod}\n' f'{src_mod}\n'
f'Connection success: {url!r}' f'Connection success: {url}'
) )
# begin relay loop to forward msgs # begin relay loop to forward msgs
@ -341,7 +341,7 @@ async def _reconnect_forever(
async def open_autorecon_ws( async def open_autorecon_ws(
url: str, url: str,
fixture: AsyncContextManager|None = None, fixture: AsyncContextManager | None = None,
# time in sec between msgs received before # time in sec between msgs received before
# we presume connection might need a reset. # we presume connection might need a reset.
@ -361,7 +361,7 @@ async def open_autorecon_ws(
and restarts the full http(s) handshake on catches of certain and restarts the full http(s) handshake on catches of certain
connetivity errors, or some user defined recv timeout. connetivity errors, or some user defined recv timeout.
You can provide a `fixture` async-context-manager which will be You can provide a ``fixture`` async-context-manager which will be
entered/exitted around each connection reset; eg. for entered/exitted around each connection reset; eg. for
(re)requesting subscriptions without requiring streaming setup (re)requesting subscriptions without requiring streaming setup
code to rerun. code to rerun.
@ -402,8 +402,7 @@ async def open_autorecon_ws(
except NoBsWs.recon_errors as con_err: except NoBsWs.recon_errors as con_err:
log.warning( log.warning(
f'Entire ws-channel disconnect due to,\n' f'Entire ws-channel disconnect due to,\n'
f'\n' f'con_err: {con_err!r}\n'
f'{con_err!r}\n'
) )
@ -425,7 +424,7 @@ class JSONRPCResult(Struct):
async def open_jsonrpc_session( async def open_jsonrpc_session(
url: str, url: str,
start_id: int = 0, start_id: int = 0,
response_type: Type[Struct] = JSONRPCResult, response_type: type = JSONRPCResult,
msg_recv_timeout: float = float('inf'), msg_recv_timeout: float = float('inf'),
# ^NOTE, since only `deribit` is using this jsonrpc stuff atm # ^NOTE, since only `deribit` is using this jsonrpc stuff atm
# and options mkts are generally "slow moving".. # and options mkts are generally "slow moving"..
@ -436,10 +435,7 @@ async def open_jsonrpc_session(
# broken and never restored with wtv init sequence is required to # broken and never restored with wtv init sequence is required to
# re-establish a working req-resp session. # re-establish a working req-resp session.
) -> Callable[ ) -> Callable[[str, dict], dict]:
[str, dict],
dict,
]:
''' '''
Init a json-RPC-over-websocket connection to the provided `url`. Init a json-RPC-over-websocket connection to the provided `url`.
@ -535,18 +531,14 @@ async def open_jsonrpc_session(
'id': mid, 'id': mid,
} if not rpc_results.get(mid): } if not rpc_results.get(mid):
log.warning( log.warning(
f'Unexpected ws msg?\n' f'Unexpected ws msg: {json.dumps(msg, indent=4)}'
f'{json.dumps(msg, indent=4)}'
) )
case { case {
'method': _, 'method': _,
'params': _, 'params': _,
}: }:
log.debug( log.debug(f'Recieved\n{msg}')
f'Recieved\n'
f'{msg!r}'
)
case { case {
'error': error 'error': error
@ -562,15 +554,12 @@ async def open_jsonrpc_session(
result['event'].set() result['event'].set()
log.error( log.error(
f'JSONRPC request failed\n' f'JSONRPC request failed\n'
f'req: {req_msg!r}\n' f'req: {req_msg}\n'
f'resp: {error!r}\n' f'resp: {error}\n'
) )
case _: case _:
log.warning( log.warning(f'Unhandled JSON-RPC msg!?\n{msg}')
f'Unhandled JSON-RPC msg!?\n'
f'{msg!r}'
)
tn.start_soon(recv_task) tn.start_soon(recv_task)
yield json_rpc yield json_rpc

View File

@ -342,10 +342,7 @@ class MainWindow(QMainWindow):
log.debug('Saved window geometry for next session') log.debug('Saved window geometry for next session')
# raising KBI seems to get intercepted by by Qt so just use the system. # raising KBI seems to get intercepted by by Qt so just use the system.
os.kill( os.kill(os.getpid(), signal.SIGINT)
os.getpid(),
signal.SIGINT,
)
@property @property
def status_bar(self) -> QStatusBar: def status_bar(self) -> QStatusBar:
@ -552,9 +549,9 @@ class MainWindow(QMainWindow):
# update godwidget and its children # update godwidget and its children
if self.godwidget: if self.godwidget:
# update search bg if it exists # update search widget if it exists
if search := getattr(self.godwidget, 'search', None): if hasattr(self.godwidget, 'search') and self.godwidget.search:
search.update_fonts() self.godwidget.search.update_fonts()
# update order mode panes in all chart views # update order mode panes in all chart views
self._update_chart_order_panes() self._update_chart_order_panes()
@ -574,10 +571,7 @@ class MainWindow(QMainWindow):
return return
# iterate through all linked splits (hist and rt) # iterate through all linked splits (hist and rt)
for splits_name in [ for splits_name in ['hist_linked', 'rt_linked']:
'hist_linked',
'rt_linked',
]:
splits = getattr(self.godwidget, splits_name, None) splits = getattr(self.godwidget, splits_name, None)
if not splits: if not splits:
continue continue
@ -589,14 +583,12 @@ class MainWindow(QMainWindow):
self._update_chart_axes(chart) self._update_chart_axes(chart)
# update order pane # update order pane
if ( if hasattr(chart, 'view'):
(view := getattr(chart, 'view', None)) view = chart.view
and if hasattr(view, 'order_mode') and view.order_mode:
(order_mode := getattr(view, 'order_mode', None)) order_mode = view.order_mode
and if hasattr(order_mode, 'pane') and order_mode.pane:
(pane := getattr(order_mode, 'pane', None)) order_mode.pane.update_fonts()
):
pane.update_fonts()
# also check subplots # also check subplots
subplots = getattr(splits, 'subplots', {}) subplots = getattr(splits, 'subplots', {})
@ -605,90 +597,54 @@ class MainWindow(QMainWindow):
self._update_chart_axes(subplot_chart) self._update_chart_axes(subplot_chart)
# update subplot order pane # update subplot order pane
if ( if hasattr(subplot_chart, 'view'):
(view := getattr(subplot_chart, 'view', None)) subplot_view = subplot_chart.view
and if hasattr(subplot_view, 'order_mode') and subplot_view.order_mode:
(order_mode := getattr( subplot_order_mode = subplot_view.order_mode
view, 'order_mode', None, if hasattr(subplot_order_mode, 'pane') and subplot_order_mode.pane:
)) subplot_order_mode.pane.update_fonts()
and
(pane := getattr(order_mode, 'pane', None))
):
pane.update_fonts()
# resize all sidepanes to match the # resize all sidepanes to match main chart's sidepane width
# main chart's sidepane width; ensures # this ensures volume/subplot sidepanes match the main chart
# volume/subplot sidepanes match. if splits and hasattr(splits, 'resize_sidepanes'):
if ( splits.resize_sidepanes()
splits
and
(resize := getattr(
splits, 'resize_sidepanes', None,
))
):
resize()
def _update_chart_axes(self, chart) -> None: def _update_chart_axes(self, chart) -> None:
''' '''Update axis fonts and sizing for a chart.'''
Update axis fonts and sizing for a chart.
'''
from . import _style from . import _style
# update price axis (right side) # update price axis (right side)
if plot_item := getattr(chart, 'pi', None): if hasattr(chart, 'pi') and chart.pi:
plot_item = chart.pi
# get all axes from plot item # get all axes from plot item
for axis_name in [ for axis_name in ['left', 'right', 'bottom', 'top']:
'left', axis = plot_item.getAxis(axis_name)
'right', if axis and hasattr(axis, 'update_fonts'):
'bottom', axis.update_fonts(_style._font)
'top',
]:
if (
(axis := plot_item.getAxis(axis_name))
and
(update_fonts := getattr(
axis, 'update_fonts', None,
))
):
update_fonts(_style._font)
# force plot item to recalculate # force plot item to recalculate its entire layout
# its entire layout
plot_item.updateGeometry() plot_item.updateGeometry()
# force chart widget to update # force chart widget to update
if update_geom := getattr(chart, 'updateGeometry', None): if hasattr(chart, 'updateGeometry'):
update_geom() chart.updateGeometry()
# trigger a full scene update # trigger a full scene update
if update := getattr(chart, 'update', None): if hasattr(chart, 'update'):
update() chart.update()
def _refresh_widget_fonts( def _refresh_widget_fonts(self, widget: QWidget) -> None:
self,
widget: QWidget,
) -> None:
''' '''
Recursively update font sizes in all Recursively update font sizes in all child widgets.
child widgets.
Handles widgets that have font-size
hardcoded in their stylesheets.
This handles widgets that have font-size hardcoded in their stylesheets.
''' '''
from . import _style from . import _style
# recursively process all children # recursively process all children
for child in widget.findChildren(QWidget): for child in widget.findChildren(QWidget):
# skip widgets that have custom update # skip widgets that have their own update_fonts method (handled separately)
# methods; handled separately below. if hasattr(child, 'update_fonts'):
if getattr(child, 'update_fonts', None):
log.debug(
f'Skipping sub-widget with'
f' custom font-update meth..\n'
f'{child!r}\n'
)
continue continue
# update child's stylesheet if it has font-size # update child's stylesheet if it has font-size
@ -698,35 +654,23 @@ class MainWindow(QMainWindow):
# this is a heuristic - may need refinement # this is a heuristic - may need refinement
try: try:
child.setFont(_style._font.font) child.setFont(_style._font.font)
except ( except (AttributeError, RuntimeError):
AttributeError, pass
RuntimeError,
):
log.exception(
'Failed to update sub-widget font?\n'
)
# update child's font # update child's font
try: try:
child.setFont(_style._font.font) child.setFont(_style._font.font)
except ( except (AttributeError, RuntimeError):
AttributeError, pass
RuntimeError,
):
log.exception(
'Failed to update sub-widget font?\n'
)
# singleton app per actor # singleton app per actor
_qt_win: QMainWindow|None = None _qt_win: QMainWindow = None
def main_window() -> MainWindow: def main_window() -> MainWindow:
''' 'Return the actor-global Qt window.'
Return the actor-global Qt window.
'''
global _qt_win global _qt_win
assert _qt_win assert _qt_win
return _qt_win return _qt_win