Position pane UI improvements

- pass label text and field widget key separately
- fix fill status bar slot sizing logic (once and for all) and
  create a new type that allows generating / resizing the bar's
  size / values with a `.set_slots()` method
- pull account names from allocator attr
- set `.fill_bar` as the fill status bar on the form for now
fsp_feeds
Tyler Goodlet 2021-08-17 14:08:25 -04:00
parent 87bd6046e5
commit 7192264818
1 changed files with 103 additions and 72 deletions

View File

@ -22,7 +22,6 @@ from __future__ import annotations
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from functools import partial from functools import partial
from textwrap import dedent from textwrap import dedent
import math
from typing import Optional from typing import Optional
import trio import trio
@ -247,13 +246,14 @@ class FieldsForm(QWidget):
def add_edit_field( def add_edit_field(
self, self,
name: str, key: str,
label_name: str,
value: str, value: str,
) -> FontAndChartAwareLineEdit: ) -> FontAndChartAwareLineEdit:
# TODO: maybe a distint layout per "field" item? # TODO: maybe a distint layout per "field" item?
label = self.add_field_label(name) label = self.add_field_label(label_name)
edit = FontAndChartAwareLineEdit( edit = FontAndChartAwareLineEdit(
parent=self, parent=self,
@ -268,23 +268,24 @@ class FieldsForm(QWidget):
edit.setText(str(value)) edit.setText(str(value))
self.form.addRow(label, edit) self.form.addRow(label, edit)
self.fields[name] = edit self.fields[key] = edit
return edit return edit
def add_select_field( def add_select_field(
self, self,
name: str, key: str,
label_name: str,
values: list[str], values: list[str],
) -> QComboBox: ) -> QComboBox:
# TODO: maybe a distint layout per "field" item? # TODO: maybe a distint layout per "field" item?
label = self.add_field_label(name) label = self.add_field_label(label_name)
select = QComboBox(self) select = QComboBox(self)
select._key = name select._key = key
for i, value in enumerate(values): for i, value in enumerate(values):
select.insertItem(i, str(value)) select.insertItem(i, str(value))
@ -365,7 +366,13 @@ async def handle_field_input(
if key in (Qt.Key_Enter, Qt.Key_Return): if key in (Qt.Key_Enter, Qt.Key_Return):
value = widget.text() value = widget.text()
key = widget._key key = widget._key
old = getattr(model, key)
try:
setattr(model, key, value) setattr(model, key, value)
except pydantic.error_wrappers.ValidationError:
setattr(model, key, old)
widget.setText(str(old))
print(model.dict()) print(model.dict())
@ -383,13 +390,14 @@ def mk_form(
form.model = model form.model = model
# generate sub-components from schema dict # generate sub-components from schema dict
for name, config in fields_schema.items(): for key, config in fields_schema.items():
wtype = config['type'] wtype = config['type']
label = str(config.get('label', name)) label = str(config.get('label', key))
# plain (line) edit field # plain (line) edit field
if wtype == 'edit': if wtype == 'edit':
w = form.add_edit_field( w = form.add_edit_field(
key,
label, label,
config['default_value'] config['default_value']
) )
@ -398,17 +406,24 @@ def mk_form(
elif wtype == 'select': elif wtype == 'select':
values = list(config['default_value']) values = list(config['default_value'])
w = form.add_select_field( w = form.add_select_field(
key,
label, label,
values values
) )
def write_model(text: str): def write_model(text: str, key: str):
print(f'{text}') print(f'{text}')
setattr(form.model, name, text) setattr(form.model, key, text)
print(form.model)
w.currentTextChanged.connect(write_model) w.currentTextChanged.connect(
partial(
write_model,
key=key,
)
)
w._key = name w._key = key
return form return form
@ -509,34 +524,31 @@ def mk_fill_status_bar(
font_color='gunmetal', font_color='gunmetal',
) )
bar = QProgressBar(fields) class FillStatusBar(QProgressBar):
# slots: int = 4
approx_h = 8/3 * w
border_size_px: int = 2
slot_margin_px: int = 2
hbox.addWidget(bar, alignment=Qt.AlignLeft | Qt.AlignTop) def set_slots(
self,
slots: int,
value: float,
bar_labels_rhs_vbox = QVBoxLayout(fields) ) -> None:
bar_labels_rhs_vbox.addWidget(
top_label,
alignment=Qt.AlignLeft | Qt.AlignTop
)
bar_labels_rhs_vbox.addWidget(
bottom_label,
alignment=Qt.AlignLeft | Qt.AlignBottom
)
hbox.addLayout(bar_labels_rhs_vbox)
# compute "chunk" sizes for fill-status-bar based on some static height
slots = 4
border_size_px = 2
slot_margin_px = 2
# TODO: compute "used height" thus far and mostly fill the rest # TODO: compute "used height" thus far and mostly fill the rest
slot_height_px = math.floor( approx_h = self.approx_h
(bar_h - 2*border_size_px)/slots tot_slot_h, r = divmod(
) - slot_margin_px*1 approx_h,
slots,
)
clipped = slots * tot_slot_h + 2*self.border_size_px
self.setMaximumHeight(clipped)
slot_height_px = tot_slot_h - 2*self.slot_margin_px
bar.setOrientation(Qt.Vertical) self.setOrientation(Qt.Vertical)
bar.setStyleSheet( self.setStyleSheet(
f""" f"""
QProgressBar {{ QProgressBar {{
@ -570,13 +582,34 @@ def mk_fill_status_bar(
# color: #19232D; # color: #19232D;
# width: 10px; # width: 10px;
bar.setRange(0, slots) self.setRange(0, slots)
bar.setValue(1) self.setValue(value)
bar.setFormat('') self.setFormat('') # label format
bar.setMinimumHeight(bar_h) self.setMinimumWidth(h * 1.375)
bar.setMaximumHeight(bar_h + slots*slot_margin_px) self.setMaximumWidth(h * 1.375)
bar.setMinimumWidth(h * 1.375)
bar.setMaximumWidth(h * 1.375) bar = FillStatusBar(fields)
hbox.addWidget(bar, alignment=Qt.AlignLeft | Qt.AlignTop)
bar_labels_rhs_vbox = QVBoxLayout(fields)
bar_labels_rhs_vbox.addWidget(
top_label,
alignment=Qt.AlignLeft | Qt.AlignTop
)
bar_labels_rhs_vbox.addWidget(
bottom_label,
alignment=Qt.AlignLeft | Qt.AlignBottom
)
hbox.addLayout(bar_labels_rhs_vbox)
# compute "chunk" sizes for fill-status-bar based on some static height
slots = 4
border_size_px = 2
slot_margin_px = 2
bar.set_slots(slots, value=0)
return hbox, bar return hbox, bar
@ -592,29 +625,25 @@ def mk_order_pane_layout(
# TODO: some kinda pydantic sub-type # TODO: some kinda pydantic sub-type
# that enforces a composite widget attr er sumthin.. # that enforces a composite widget attr er sumthin..
# as part of our ORM thingers. # as part of our ORM thingers.
allocator = mk_pp_alloc() alloc = mk_pp_alloc()
# TODO: maybe just allocate the whole fields form here # TODO: maybe just allocate the whole fields form here
# and expect an async ctx entry? # and expect an async ctx entry?
form = mk_form( form = mk_form(
parent=parent, parent=parent,
model=allocator, model=alloc,
fields_schema={ fields_schema={
'account': { 'account': {
'type': 'select', 'type': 'select',
'default_value': [ 'default_value': alloc._accounts.keys()
'paper',
# 'ib.margin',
# 'ib.paper',
],
}, },
'size_unit': { 'size_unit': {
'label': '**allocate**:', 'label': '**allocate**:',
'type': 'select', 'type': 'select',
'default_value': [ 'default_value': [
'$ size', '$ size',
'# units',
'% of port', '% of port',
'# shares'
], ],
}, },
'disti_weight': { 'disti_weight': {
@ -628,13 +657,14 @@ def mk_order_pane_layout(
'default_value': 5000, 'default_value': 5000,
}, },
'slots': { 'slots': {
'label': '**slots**:',
'type': 'edit', 'type': 'edit',
'default_value': 4, 'default_value': 4,
}, },
}, },
) )
form._font_size = font_size form._font_size = font_size
allocator._widget = form alloc._widget = form
# top level pane layout # top level pane layout
# XXX: see ``FieldsForm.__init__()`` for why we can't do basic # XXX: see ``FieldsForm.__init__()`` for why we can't do basic
@ -644,7 +674,8 @@ def mk_order_pane_layout(
# _, h = form.width(), form.height() # _, h = form.width(), form.height()
# print(f'w, h: {w, h}') # print(f'w, h: {w, h}')
hbox, bar = mk_fill_status_bar(form, pane_vbox=vbox) hbox, fill_bar = mk_fill_status_bar(form, pane_vbox=vbox)
form.fill_bar = fill_bar
# add pp fill bar + spacing # add pp fill bar + spacing
vbox.addLayout(hbox, stretch=1/3) vbox.addLayout(hbox, stretch=1/3)