WIP add a lambda-QFrame to get per chart sidpanes for each linkedsplits row

fsp_feeds
Tyler Goodlet 2021-07-27 06:09:40 -04:00
parent 825680b8c6
commit 63138ccbf4
1 changed files with 197 additions and 78 deletions

View File

@ -26,6 +26,11 @@ from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtCore import QEvent from PyQt5.QtCore import QEvent
from PyQt5.QtWidgets import (
QFrame,
QWidget,
# QSizePolicy,
)
import numpy as np import numpy as np
import pyqtgraph as pg import pyqtgraph as pg
import tractor import tractor
@ -68,13 +73,13 @@ from ._interaction import ChartView
from .order_mode import run_order_mode from .order_mode import run_order_mode
from .. import fsp from .. import fsp
from ..data import feed from ..data import feed
from ._forms import FieldsForm, open_form from ._forms import FieldsForm, open_form, mk_health_bar
log = get_logger(__name__) log = get_logger(__name__)
class GodWidget(QtWidgets.QWidget): class GodWidget(QWidget):
''' '''
"Our lord and savior, the holy child of window-shua, there is no "Our lord and savior, the holy child of window-shua, there is no
widget above thee." - 6|6 widget above thee." - 6|6
@ -236,7 +241,7 @@ class GodWidget(QtWidgets.QWidget):
return order_mode_started return order_mode_started
class LinkedSplits(QtWidgets.QWidget): class LinkedSplits(QWidget):
''' '''
Widget that holds a central chart plus derived Widget that holds a central chart plus derived
subcharts computed from the original data set apart subcharts computed from the original data set apart
@ -338,12 +343,16 @@ class LinkedSplits(QtWidgets.QWidget):
linkedsplits=self, linkedsplits=self,
digits=symbol.digits(), digits=symbol.digits(),
) )
self.chart = self.add_plot( self.chart = self.add_plot(
name=symbol.key, name=symbol.key,
array=array, array=array,
# xaxis=self.xaxis, # xaxis=self.xaxis,
style=style, style=style,
_is_main=True, _is_main=True,
sidepane=self.godwidget.pp_config,
) )
# add crosshair graphic # add crosshair graphic
self.chart.addItem(self.cursor) self.chart.addItem(self.cursor)
@ -353,7 +362,10 @@ class LinkedSplits(QtWidgets.QWidget):
self.chart.hideAxis('bottom') self.chart.hideAxis('bottom')
# style? # style?
self.chart.setFrameStyle(QtWidgets.QFrame.StyledPanel | QtWidgets.QFrame.Plain) self.chart.setFrameStyle(
QtWidgets.QFrame.StyledPanel |
QtWidgets.QFrame.Plain
)
return self.chart return self.chart
@ -368,6 +380,8 @@ class LinkedSplits(QtWidgets.QWidget):
style: str = 'line', style: str = 'line',
_is_main: bool = False, _is_main: bool = False,
sidepane: Optional[QWidget] = None,
**cpw_kwargs, **cpw_kwargs,
) -> 'ChartPlotWidget': ) -> 'ChartPlotWidget':
@ -398,6 +412,53 @@ class LinkedSplits(QtWidgets.QWidget):
self.xaxis.hide() self.xaxis.hide()
self.xaxis = xaxis self.xaxis = xaxis
if sidepane:
class ChartandPane(QFrame):
def __init__(
self,
parent=None,
) -> None:
super().__init__(parent)
# self.chart = cpw
self.sidepane = sidepane
# cpw.chart_and_pane = self
# self.setStyleSheet(
# f"""QFrame {{
# color : {hcolor('default_darkest')};
# background-color : {hcolor('default_darkest')};
# }}
# """
# )
hbox = self.hbox = QtGui.QHBoxLayout(self)
hbox.setAlignment(Qt.AlignTop | Qt.AlignLeft)
hbox.setContentsMargins(0, 0, 0, 0)
hbox.setSpacing(3)
def sizeHint(self) -> QtCore.QSize:
return self.chart.sizeHint() + sidepane.sizeHint()
paned_chart = ChartandPane(parent=self.splitter)
# splitter_widget = QWidget(self)
# splitter_widget.setSizePolicy(
# QSizePolicy.Expanding,
# QSizePolicy.Expanding,
# )
# hbox = QtGui.QHBoxLayout(splitter_widget)
# hbox.setAlignment(Qt.AlignTop | Qt.AlignRight)
# hbox.setContentsMargins(0, 0, 0, 0)
# hbox.setSpacing(3)
# else:
# splitter_widget = cpw
cpw = ChartPlotWidget( cpw = ChartPlotWidget(
# this name will be used to register the primary # this name will be used to register the primary
@ -406,7 +467,8 @@ class LinkedSplits(QtWidgets.QWidget):
data_key=array_key or name, data_key=array_key or name,
array=array, array=array,
parent=self.splitter, # parent=self.splitter,
parent=paned_chart if sidepane else self.splitter,
linkedsplits=self, linkedsplits=self,
axisItems={ axisItems={
'bottom': xaxis, 'bottom': xaxis,
@ -417,14 +479,38 @@ class LinkedSplits(QtWidgets.QWidget):
**cpw_kwargs, **cpw_kwargs,
) )
if sidepane:
paned_chart.chart = cpw
paned_chart.hbox.addWidget(cpw)
# hbox.addWidget(cpw)
paned_chart.hbox.addWidget(
sidepane,
alignment=Qt.AlignTop
)
cpw.sidepane = sidepane
# splitter_widget.setMinimumHeight(cpw.height())
# splitter_widget.setMinimumWidth(cpw.width())
# splitter_widget.show()
# hbox.addWidget(cpw)
# hbox.addWidget(sidepane)
# cpw.sidepane = sidepane
# cpw.hbox = hbox
# give viewbox as reference to chart # give viewbox as reference to chart
# allowing for kb controls and interactions on **this** widget # allowing for kb controls and interactions on **this** widget
# (see our custom view mode in `._interactions.py`) # (see our custom view mode in `._interactions.py`)
cv.chart = cpw cv.chart = cpw
cpw.plotItem.vb.linkedsplits = self cpw.plotItem.vb.linkedsplits = self
cpw.setFrameStyle(QtWidgets.QFrame.StyledPanel) # | QtWidgets.QFrame.Plain) cpw.setFrameStyle(
QtWidgets.QFrame.StyledPanel
# | QtWidgets.QFrame.Plain)
)
cpw.hideButtons() cpw.hideButtons()
# XXX: gives us outline on backside of y-axis # XXX: gives us outline on backside of y-axis
cpw.getPlotItem().setContentsMargins(*CHART_MARGINS) cpw.getPlotItem().setContentsMargins(*CHART_MARGINS)
@ -448,8 +534,8 @@ class LinkedSplits(QtWidgets.QWidget):
# track by name # track by name
self.subplots[name] = cpw self.subplots[name] = cpw
# XXX: we need this right? sidepane.setMinimumWidth(self.chart.sidepane.width())
self.splitter.addWidget(cpw) self.splitter.addWidget(paned_chart if sidepane else cpw)
# scale split regions # scale split regions
self.set_split_sizes() self.set_split_sizes()
@ -832,7 +918,11 @@ class ChartPlotWidget(pg.PlotWidget):
# one place to dig around this might be the `QBackingStore` # one place to dig around this might be the `QBackingStore`
# https://doc.qt.io/qt-5/qbackingstore.html # https://doc.qt.io/qt-5/qbackingstore.html
# curve.setData(y=array[name], x=array['index'], **kwargs) # curve.setData(y=array[name], x=array['index'], **kwargs)
curve.update_from_array(x=array['index'], y=array[data_key], **kwargs) curve.update_from_array(
x=array['index'],
y=array[data_key],
**kwargs
)
return curve return curve
@ -969,7 +1059,7 @@ class ChartPlotWidget(pg.PlotWidget):
self.scene().leaveEvent(ev) self.scene().leaveEvent(ev)
_clear_throttle_rate: int = 50 # Hz _clear_throttle_rate: int = 35 # Hz
_book_throttle_rate: int = 16 # Hz _book_throttle_rate: int = 16 # Hz
@ -1279,7 +1369,8 @@ async def run_fsp(
group_key=group_status_key, group_key=group_status_key,
) )
async with portal.open_stream_from( async with (
portal.open_stream_from(
# subactor entrypoint # subactor entrypoint
fsp.cascade, fsp.cascade,
@ -1291,7 +1382,28 @@ async def run_fsp(
symbol=sym, symbol=sym,
fsp_func_name=fsp_func_name, fsp_func_name=fsp_func_name,
) as stream: ) as stream,
open_form(
godwidget=linkedsplits.godwidget,
parent=linkedsplits.godwidget,
fields={
'name': {
'key': '**fsp**:',
'type': 'select',
'default_value': [
f'{display_name}'
],
},
'period': {
'key': '**period (bars)**:',
'type': 'edit',
'default_value': 14,
},
},
) as sidepane,
):
# receive last index for processed historical # receive last index for processed historical
# data-array as first msg # data-array as first msg
@ -1315,6 +1427,7 @@ async def run_fsp(
array=shm.array, array=shm.array,
array_key=conf['fsp_func_name'], array_key=conf['fsp_func_name'],
sidepane=sidepane,
# curve by default # curve by default
ohlc=False, ohlc=False,
@ -1349,7 +1462,11 @@ async def run_fsp(
# works also for overlays in which case data is looked up from # works also for overlays in which case data is looked up from
# internal chart array set.... # internal chart array set....
chart.update_curve_from_array(display_name, shm.array, array_key=fsp_func_name) chart.update_curve_from_array(
display_name,
shm.array,
array_key=fsp_func_name
)
# TODO: figure out if we can roll our own `FillToThreshold` to # TODO: figure out if we can roll our own `FillToThreshold` to
# get brush filled polygons for OS/OB conditions. # get brush filled polygons for OS/OB conditions.
@ -1707,10 +1824,68 @@ async def _async_main(
sbar = godwidget.window.status_bar sbar = godwidget.window.status_bar
starting_done = sbar.open_status('starting ze sexy chartz') starting_done = sbar.open_status('starting ze sexy chartz')
async with trio.open_nursery() as root_n: async with (
trio.open_nursery() as root_n,
open_form(
godwidget=godwidget,
parent=godwidget,
fields={
'account': {
'key': '**account**:',
'type': 'select',
'default_value': [
'piker.paper',
# 'ib.margin',
# 'ib.paper',
],
},
'disti_policy': {
'key': '**entry policy**:',
'type': 'select',
'default_value': ['uniform'],
},
'slots': {
'key': '**slots**:',
'type': 'edit',
'default_value': 4,
},
'allocator': {
'key': '**allocator**:',
'type': 'select',
'default_value': ['$ size', '% of port', '# shares'],
},
'dollar_size': {
'key': '**$size**:',
'type': 'edit',
'default_value': '5k',
},
},
) as pp_config,
):
pp_config: FieldsForm
mk_health_bar(pp_config)
pp_config.show()
# add as next-to-y-axis pane
godwidget.pp_config = pp_config
# godwidget.hbox.insertWidget(
# 1,
# pp_config,
# # alights to top and uses minmial space based on
# # search bar size hint (i think?)
# alignment=Qt.AlignTop
# )
godwidget.hbox.setAlignment(Qt.AlignTop)
# sb = god_widget.window.statusBar()
# sb.insertPermanentWidget(0, pp_config)
# pp_config.show()
# set root nursery and task stack for spawning other charts/feeds # set root nursery and task stack for spawning other charts/feeds
# that run cached in the bg # that run cached in the bg
godwidget._root_n = root_n godwidget._root_n = root_n
# setup search widget and focus main chart view at startup # setup search widget and focus main chart view at startup
@ -1761,63 +1936,7 @@ async def _async_main(
# let key repeats pass through for search # let key repeats pass through for search
filter_auto_repeats=False, filter_auto_repeats=False,
), ),
open_form(
godwidget=godwidget,
parent=godwidget,
fields={
'account': {
'key': '**account**:',
'type': 'select',
'default_value': [
'piker.paper',
# 'ib.margin',
# 'ib.paper',
],
},
'disti_policy': {
'key': '**entry policy**:',
'type': 'select',
'default_value': ['uniform'],
},
'slots': {
'key': '**slots**:',
'type': 'edit',
'default_value': 4,
},
'allocator': {
'key': '**allocator**:',
'type': 'select',
'default_value': ['$ size', '% of port', '# shares'],
},
'dollar_size': {
'key': '**$size**:',
'type': 'edit',
'default_value': '5k',
},
},
) as pp_config,
): ):
pp_config: FieldsForm
pp_config.show()
# add as next-to-y-axis pane
godwidget.pp_config = pp_config
godwidget.hbox.insertWidget(
1,
pp_config,
# alights to top and uses minmial space based on
# search bar size hint (i think?)
alignment=Qt.AlignTop
)
godwidget.hbox.setAlignment(Qt.AlignTop)
# sb = god_widget.window.statusBar()
# sb.insertPermanentWidget(0, pp_config)
# pp_config.show()
# remove startup status text # remove startup status text
starting_done() starting_done()
await trio.sleep_forever() await trio.sleep_forever()