Use `QFormLayout` instead of rolling our own; add pp and feed status sections
parent
318f3b45c5
commit
e005c8b345
|
@ -18,9 +18,11 @@
|
||||||
Text entry "forms" widgets (mostly for configuration and UI user input).
|
Text entry "forms" widgets (mostly for configuration and UI user input).
|
||||||
|
|
||||||
'''
|
'''
|
||||||
from functools import partial
|
|
||||||
from typing import Optional
|
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
from functools import partial
|
||||||
|
from textwrap import dedent
|
||||||
|
import math
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
import trio
|
import trio
|
||||||
from PyQt5 import QtCore, QtGui
|
from PyQt5 import QtCore, QtGui
|
||||||
|
@ -163,23 +165,32 @@ class FieldsForm(QWidget):
|
||||||
|
|
||||||
# size it as we specify
|
# size it as we specify
|
||||||
self.setSizePolicy(
|
self.setSizePolicy(
|
||||||
QSizePolicy.Fixed,
|
QSizePolicy.Expanding,
|
||||||
QSizePolicy.Fixed,
|
QSizePolicy.Expanding,
|
||||||
)
|
)
|
||||||
# self.setMaximumHeight(30)
|
# self.setMaximumHeight(30)
|
||||||
self.setMaximumWidth(166)
|
# self.setMaximumWidth(166)
|
||||||
|
|
||||||
# split layout for the (label:| text bar entry)
|
|
||||||
self.vbox = QtGui.QVBoxLayout(self)
|
self.vbox = QtGui.QVBoxLayout(self)
|
||||||
self.vbox.setAlignment(Qt.AlignTop)
|
self.vbox.setAlignment(Qt.AlignTop)
|
||||||
self.vbox.setContentsMargins(0, 0, 4, 0)
|
self.vbox.setContentsMargins(0, 4, 4, 0)
|
||||||
self.vbox.setSpacing(3)
|
self.vbox.setSpacing(0)
|
||||||
|
|
||||||
|
# split layout for the (<label>: |<widget>|) parameters entry
|
||||||
|
self.form = QtGui.QFormLayout(self)
|
||||||
|
self.form.setAlignment(Qt.AlignCenter | Qt.AlignLeft)
|
||||||
|
self.form.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.form.setSpacing(4)
|
||||||
|
self.form.setHorizontalSpacing(0)
|
||||||
# self.vbox.addStretch()
|
# self.vbox.addStretch()
|
||||||
|
|
||||||
|
self.vbox.addLayout(self.form)
|
||||||
|
|
||||||
self.labels: dict[str, QLabel] = {}
|
self.labels: dict[str, QLabel] = {}
|
||||||
self.fields: dict[str, QWidget] = {}
|
self.fields: dict[str, QWidget] = {}
|
||||||
|
|
||||||
self._font_size = _font_small.px_size - 2
|
self._font_size = _font_small.px_size - 2
|
||||||
|
self.vbox.addSpacing(0.375 * self._font_size)
|
||||||
self._max_item_width: (float, float) = 0, 0
|
self._max_item_width: (float, float) = 0, 0
|
||||||
|
|
||||||
def add_field_label(
|
def add_field_label(
|
||||||
|
@ -193,9 +204,7 @@ class FieldsForm(QWidget):
|
||||||
) -> QtGui.QLabel:
|
) -> QtGui.QLabel:
|
||||||
|
|
||||||
# add label to left of search bar
|
# add label to left of search bar
|
||||||
self.label = label = QtGui.QLabel(parent=self)
|
self.label = label = QtGui.QLabel()
|
||||||
label.setTextFormat(Qt.MarkdownText) # markdown
|
|
||||||
label.setFont(_font.font)
|
|
||||||
font_size = font_size or self._font_size - 3
|
font_size = font_size or self._font_size - 3
|
||||||
label.setStyleSheet(
|
label.setStyleSheet(
|
||||||
f"""QLabel {{
|
f"""QLabel {{
|
||||||
|
@ -204,7 +213,9 @@ class FieldsForm(QWidget):
|
||||||
}}
|
}}
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
label.setMargin(4)
|
label.setFont(_font.font)
|
||||||
|
label.setTextFormat(Qt.MarkdownText) # markdown
|
||||||
|
label.setMargin(0)
|
||||||
|
|
||||||
label.setText(name)
|
label.setText(name)
|
||||||
|
|
||||||
|
@ -212,9 +223,8 @@ class FieldsForm(QWidget):
|
||||||
QtCore.Qt.AlignVCenter
|
QtCore.Qt.AlignVCenter
|
||||||
| QtCore.Qt.AlignLeft
|
| QtCore.Qt.AlignLeft
|
||||||
)
|
)
|
||||||
label.show()
|
|
||||||
|
|
||||||
self.vbox.addWidget(label, **vbox_kwargs)
|
# self.vbox.addWidget(label, **vbox_kwargs)
|
||||||
self.labels[name] = label
|
self.labels[name] = label
|
||||||
|
|
||||||
return label
|
return label
|
||||||
|
@ -230,12 +240,10 @@ class FieldsForm(QWidget):
|
||||||
) -> FontAndChartAwareLineEdit:
|
) -> FontAndChartAwareLineEdit:
|
||||||
|
|
||||||
# TODO: maybe a distint layout per "field" item?
|
# TODO: maybe a distint layout per "field" item?
|
||||||
self.add_field_label(name)
|
label = self.add_field_label(name)
|
||||||
|
|
||||||
edit = FontAndChartAwareLineEdit(
|
edit = FontAndChartAwareLineEdit(
|
||||||
parent=self,
|
parent=self,
|
||||||
# parent_chart=self.godwidget,
|
|
||||||
# width_in_chars=6,
|
|
||||||
)
|
)
|
||||||
edit.setStyleSheet(
|
edit.setStyleSheet(
|
||||||
f"""QLineEdit {{
|
f"""QLineEdit {{
|
||||||
|
@ -245,7 +253,8 @@ class FieldsForm(QWidget):
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
edit.setText(str(value))
|
edit.setText(str(value))
|
||||||
self.vbox.addWidget(edit)
|
# self.vbox.addWidget(edit)
|
||||||
|
self.form.addRow(label, edit)
|
||||||
|
|
||||||
self.fields[name] = edit
|
self.fields[name] = edit
|
||||||
|
|
||||||
|
@ -260,7 +269,7 @@ class FieldsForm(QWidget):
|
||||||
) -> QComboBox:
|
) -> QComboBox:
|
||||||
|
|
||||||
# TODO: maybe a distint layout per "field" item?
|
# TODO: maybe a distint layout per "field" item?
|
||||||
self.add_field_label(name)
|
label = self.add_field_label(name)
|
||||||
|
|
||||||
select = QComboBox(self)
|
select = QComboBox(self)
|
||||||
|
|
||||||
|
@ -301,15 +310,16 @@ class FieldsForm(QWidget):
|
||||||
|
|
||||||
select.show()
|
select.show()
|
||||||
|
|
||||||
self.vbox.addWidget(select)
|
# self.vbox.addWidget(select)
|
||||||
|
self.form.addRow(label, select)
|
||||||
|
|
||||||
return select
|
return select
|
||||||
|
|
||||||
|
|
||||||
async def handle_field_input(
|
async def handle_field_input(
|
||||||
|
|
||||||
# chart: 'ChartPlotWidget', # noqa
|
|
||||||
widget: QWidget,
|
widget: QWidget,
|
||||||
|
# last_widget: QWidget, # had focus prior
|
||||||
recv_chan: trio.abc.ReceiveChannel,
|
recv_chan: trio.abc.ReceiveChannel,
|
||||||
form: FieldsForm,
|
form: FieldsForm,
|
||||||
|
|
||||||
|
@ -318,22 +328,20 @@ async def handle_field_input(
|
||||||
async for event, etype, key, mods, txt in recv_chan:
|
async for event, etype, key, mods, txt in recv_chan:
|
||||||
print(f'key: {key}, mods: {mods}, txt: {txt}')
|
print(f'key: {key}, mods: {mods}, txt: {txt}')
|
||||||
|
|
||||||
|
# default controls set
|
||||||
ctl = False
|
ctl = False
|
||||||
if mods == Qt.ControlModifier:
|
if mods == Qt.ControlModifier:
|
||||||
ctl = True
|
ctl = True
|
||||||
|
|
||||||
# cancel and close
|
if ctl and key in { # cancel and refocus
|
||||||
if ctl and key in {
|
|
||||||
Qt.Key_C,
|
Qt.Key_C,
|
||||||
Qt.Key_Space, # i feel like this is the "native" one
|
Qt.Key_Space, # i feel like this is the "native" one
|
||||||
Qt.Key_Alt,
|
Qt.Key_Alt,
|
||||||
}:
|
}:
|
||||||
# search.bar.unfocus()
|
# kill the widget focus and go back to main chart
|
||||||
|
|
||||||
# kill the search and focus back on main chart
|
|
||||||
widget.clearFocus()
|
widget.clearFocus()
|
||||||
form.godwidget.linkedsplits.focus()
|
form.godwidget.linkedsplits.focus()
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
@ -379,31 +387,129 @@ async def open_form(
|
||||||
|
|
||||||
|
|
||||||
def mk_health_bar(
|
def mk_health_bar(
|
||||||
form: FieldsForm,
|
fields: FieldsForm,
|
||||||
|
|
||||||
) -> FieldsForm:
|
) -> FieldsForm:
|
||||||
|
|
||||||
form.vbox.addSpacing(6)
|
# fh = form.form.sizeHint().height()
|
||||||
form.add_field_label('fill status', alignment=Qt.AlignCenter)
|
|
||||||
form.vbox.addSpacing(6)
|
|
||||||
|
|
||||||
fill_bar = QProgressBar(form)
|
w, h = fields.width(), fields.height()
|
||||||
import math
|
print(f'w, h: {w, h}')
|
||||||
|
|
||||||
|
# TODO: spec this based on remaining space?
|
||||||
|
# fields.vbox.addSpacing(36)
|
||||||
|
fields.vbox.addSpacing((5/8) * h)
|
||||||
|
|
||||||
|
# title label
|
||||||
|
pps_label = fields.add_field_label(
|
||||||
|
'pps',
|
||||||
|
font_size=_font_small.px_size,
|
||||||
|
)
|
||||||
|
|
||||||
|
# indent = 18
|
||||||
|
bracket_val = 0.375 * w
|
||||||
|
indent = bracket_val / 1.625
|
||||||
|
|
||||||
|
section_hbox = QtGui.QHBoxLayout(fields)
|
||||||
|
section_hbox.addSpacing(indent - 2) # pair with bar indent
|
||||||
|
section_hbox.addWidget(
|
||||||
|
pps_label,
|
||||||
|
alignment=Qt.AlignLeft | Qt.AlignTop,
|
||||||
|
)
|
||||||
|
|
||||||
|
# fields.vbox.addWidget(
|
||||||
|
# label,
|
||||||
|
# alignment=Qt.AlignLeft | Qt.AlignTop
|
||||||
|
# )
|
||||||
|
fields.vbox.addLayout(section_hbox)
|
||||||
|
|
||||||
|
# TODO: spec this based on remaining space?
|
||||||
|
fields.vbox.addSpacing(6)
|
||||||
|
|
||||||
|
hbox = QtGui.QHBoxLayout(fields)
|
||||||
|
|
||||||
|
hbox.addSpacing(indent) # push to right a bit
|
||||||
|
|
||||||
|
# config
|
||||||
|
hbox.setSpacing(indent * 0.375)
|
||||||
|
hbox.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
||||||
|
hbox.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
# info label on RHS
|
||||||
|
# {30}% PnL\n
|
||||||
|
|
||||||
|
# TODO: use percentage str formatter:
|
||||||
|
#https://docs.python.org/3/library/string.html#grammar-token-precision
|
||||||
|
|
||||||
|
top_label = fields.add_field_label(
|
||||||
|
dedent(f"""
|
||||||
|
{3.32:.1f}% port
|
||||||
|
"""),
|
||||||
|
font_size=_font.px_size - 5,
|
||||||
|
)
|
||||||
|
|
||||||
|
bottom_label = fields.add_field_label(
|
||||||
|
dedent(f"""
|
||||||
|
{5e3/4/1e3:.1f}k step\n
|
||||||
|
"""),
|
||||||
|
font_size=_font.px_size - 5,
|
||||||
|
)
|
||||||
|
|
||||||
|
bar = QProgressBar(fields)
|
||||||
|
hbox.addWidget(bar, alignment=Qt.AlignLeft | Qt.AlignTop)
|
||||||
|
|
||||||
|
bar_label_vbox = QtGui.QVBoxLayout(fields)
|
||||||
|
bar_label_vbox.addWidget(
|
||||||
|
top_label,
|
||||||
|
alignment=Qt.AlignLeft | Qt.AlignTop
|
||||||
|
)
|
||||||
|
bar_label_vbox.addWidget(
|
||||||
|
bottom_label,
|
||||||
|
alignment=Qt.AlignLeft | Qt.AlignBottom
|
||||||
|
)
|
||||||
|
|
||||||
|
hbox.addLayout(bar_label_vbox)
|
||||||
|
|
||||||
|
fields.vbox.addLayout(hbox) # add status bar to form
|
||||||
|
|
||||||
|
fields.vbox.addSpacing(36) # add status bar to form
|
||||||
|
|
||||||
|
feed_label = fields.add_field_label(
|
||||||
|
dedent("""
|
||||||
|
brokerd.ib\n
|
||||||
|
|_@localhost:8509\n
|
||||||
|
|_consumers: 4\n
|
||||||
|
|_streams: 9\n
|
||||||
|
"""),
|
||||||
|
font_size=_font.px_size - 5,
|
||||||
|
)
|
||||||
|
fields.vbox.addSpacing(3/8 * h)
|
||||||
|
|
||||||
|
# add feed info label
|
||||||
|
fields.vbox.addWidget(
|
||||||
|
feed_label,
|
||||||
|
alignment=Qt.AlignBottom,
|
||||||
|
)
|
||||||
|
|
||||||
|
# compute "chunk" sizes for fill-status-bar based on some static height
|
||||||
slots = 4
|
slots = 4
|
||||||
border_size_px = 2
|
border_size_px = 2
|
||||||
slot_margin_px = 2 # 1.375
|
slot_margin_px = 2
|
||||||
h = 150 #+ (2*2 + slot_margin_px*slots*2)
|
|
||||||
# multiples, r = divmod(h, slots)
|
|
||||||
slot_height_px = math.floor((h - 2*border_size_px)/slots) - slot_margin_px*1
|
|
||||||
|
|
||||||
fill_bar.setOrientation(Qt.Vertical)
|
# TODO: compute "used height" thus far and mostly fill the rest
|
||||||
fill_bar.setStyleSheet(
|
h = 200
|
||||||
|
slot_height_px = math.floor(
|
||||||
|
(h - 2*border_size_px)/slots
|
||||||
|
) - slot_margin_px*1
|
||||||
|
|
||||||
|
bar.setOrientation(Qt.Vertical)
|
||||||
|
bar.setStyleSheet(
|
||||||
f"""
|
f"""
|
||||||
QProgressBar {{
|
QProgressBar {{
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
font-size : {form._font_size - 2}px;
|
font-size : {fields._font_size - 2}px;
|
||||||
|
|
||||||
background-color: {hcolor('papas_special')};
|
background-color: {hcolor('papas_special')};
|
||||||
color : {hcolor('papas_special')};
|
color : {hcolor('papas_special')};
|
||||||
|
@ -423,35 +529,20 @@ def mk_health_bar(
|
||||||
height: {slot_height_px}px;
|
height: {slot_height_px}px;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# margin-bottom: {slot_margin_px*2}px;
|
|
||||||
# margin-top: {slot_margin_px*2}px;
|
|
||||||
# color: #19232D;
|
|
||||||
# QProgressBar::chunk:disabled {{
|
|
||||||
# background-color: #26486B;
|
|
||||||
# color: #9DA9B5;
|
|
||||||
# border-radius: 4px;
|
|
||||||
# height: 20px;
|
|
||||||
# }}
|
|
||||||
# margin-top: 3px;
|
|
||||||
# margin-bottom: 3px;
|
|
||||||
# width: 10px;
|
|
||||||
# color: #E0E1E3;
|
|
||||||
# background-color: #19232D;
|
|
||||||
|
|
||||||
# color : {hcolor('gunmetal')};
|
|
||||||
# background-color: {hcolor('bracket')};
|
|
||||||
# color: {hcolor('bracket')};
|
|
||||||
)
|
)
|
||||||
fill_bar.setRange(0, slots)
|
|
||||||
fill_bar.setValue(slots)
|
|
||||||
fill_bar.setFormat('')
|
|
||||||
fill_bar.setMinimumHeight(h)
|
|
||||||
fill_bar.setMaximumHeight(h + slots*slot_margin_px)
|
|
||||||
fill_bar.setMinimumWidth(36)
|
|
||||||
|
|
||||||
form.vbox.addWidget(fill_bar, alignment=Qt.AlignCenter)
|
# margin-bottom: {slot_margin_px*2}px;
|
||||||
|
# margin-top: {slot_margin_px*2}px;
|
||||||
|
# color: #19232D;
|
||||||
|
# width: 10px;
|
||||||
|
|
||||||
# form.vbox.addStretch()
|
bar.setRange(0, slots)
|
||||||
return form
|
bar.setValue(slots)
|
||||||
|
bar.setFormat('')
|
||||||
|
bar.setMinimumHeight(h)
|
||||||
|
bar.setMaximumHeight(h + slots*slot_margin_px)
|
||||||
|
bar.setMinimumWidth(bracket_val)
|
||||||
|
bar.setMaximumWidth(bracket_val)
|
||||||
|
|
||||||
|
return fields
|
||||||
|
|
Loading…
Reference in New Issue