Factor chart selection into widget, cleanups, add resource links

symbol_search
Tyler Goodlet 2021-05-27 11:40:08 -04:00
parent ab3adcee9e
commit 7dfc7f7fa2
1 changed files with 95 additions and 107 deletions

View File

@ -103,6 +103,18 @@ class CompleterView(QTreeView):
# https://doc.qt.io/qt-5/model-view-programming.html # https://doc.qt.io/qt-5/model-view-programming.html
# - MV tut: # - MV tut:
# https://doc.qt.io/qt-5/modelview.html # https://doc.qt.io/qt-5/modelview.html
# - custome header view (for doing stuff like we have in kivy?):
# https://doc.qt.io/qt-5/qheaderview.html#moving-header-sections
# TODO: selection model stuff for eventual aggregate feeds
# charting and mgmt;
# https://doc.qt.io/qt-5/qabstractitemview.html#setSelectionModel
# https://doc.qt.io/qt-5/qitemselectionmodel.html
# https://doc.qt.io/qt-5/modelview.html#3-2-working-with-selections
# https://doc.qt.io/qt-5/model-view-programming.html#handling-selections-of-items
# TODO: mouse extended handling:
# https://doc.qt.io/qt-5/qabstractitemview.html#entered
def __init__( def __init__(
self, self,
@ -126,6 +138,8 @@ class CompleterView(QTreeView):
# self.setUniformRowHeights(True) # self.setUniformRowHeights(True)
# self.setColumnWidth(0, 3) # self.setColumnWidth(0, 3)
# self.setVerticalBarPolicy(Qt.ScrollBarAlwaysOff)
# self.setSizeAdjustPolicy(QAbstractScrollArea.AdjustIgnored)
# ux settings # ux settings
self.setItemsExpandable(True) self.setItemsExpandable(True)
@ -133,15 +147,6 @@ class CompleterView(QTreeView):
self.setAnimated(False) self.setAnimated(False)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
# TODO: this up front?
# self.setSelectionModel(
# QItemSelectionModel.ClearAndSelect |
# QItemSelectionModel.Rows
# )
# self.setVerticalBarPolicy(Qt.ScrollBarAlwaysOff)
# self.setSizeAdjustPolicy(QAbstractScrollArea.AdjustIgnored)
# column headers # column headers
model.setHorizontalHeaderLabels(labels) model.setHorizontalHeaderLabels(labels)
@ -150,47 +155,9 @@ class CompleterView(QTreeView):
def on_pressed(self, idx: QModelIndex) -> None: def on_pressed(self, idx: QModelIndex) -> None:
search = self.parent() search = self.parent()
value = search.get_current_item() search.chart_current_item(clear_to_cache=False)
if value is not None:
provider, symbol = value
chart = search.chart_app
chart.load_symbol(
provider,
symbol,
'info',
)
# fully qualified symbol name (SNS i guess is what we're
# making?)
fqsn = '.'.join([symbol, provider]).lower()
# Re-order the symbol cache on the chart to display in
# LIFO order. this is normally only done internally by
# the chart on new symbols being loaded into memory
chart.set_chart_symbol(fqsn, chart.linkedcharts)
search.focus() search.focus()
# def viewportSizeHint(self) -> QtCore.QSize:
# vps = super().viewportSizeHint()
# return QSize(vps.width(), _font.px_size * 6 * 2)
# def sizeHint(self) -> QtCore.QSize:
# """Scale completion results up to 6/16 of window.
# """
# # height = self.window().height() * 1/6
# # psh.setHeight(self.dpi_font.px_size * 6)
# # print(_font.px_size)
# height = _font.px_size * 6 * 2
# # the default here is just the vp size without scroll bar
# # https://doc.qt.io/qt-5/qabstractscrollarea.html#viewportSizeHint
# vps = self.viewportSizeHint()
# # print(f'h: {height}\n{vps}')
# # psh.setHeight(12)
# return QSize(-1, height)
def set_font_size(self, size: int = 18): def set_font_size(self, size: int = 18):
# dpi_px_size = _font.px_size # dpi_px_size = _font.px_size
# print(size) # print(size)
@ -373,22 +340,15 @@ class CompleterView(QTreeView):
# print(f'removing {rows} from {section}') # print(f'removing {rows} from {section}')
assert model.removeRows(0, rows, parent=idx) assert model.removeRows(0, rows, parent=idx)
# remove section as well # remove section as well ?
# model.removeRow(i, QModelIndex()) # model.removeRow(i, QModelIndex())
if status_field is not None: if status_field is not None:
model.setItem(idx.row(), 1, QStandardItem(status_field)) model.setItem(idx.row(), 1, QStandardItem(status_field))
else: else:
model.setItem(idx.row(), 1, QStandardItem()) model.setItem(idx.row(), 1, QStandardItem())
# XXX: not idea how to use this
# model.setItemData(
# idx,
# {
# 0: 'cache',
# 1: 'searching',
# }
# )
self.resize() self.resize()
return idx return idx
@ -435,6 +395,11 @@ class CompleterView(QTreeView):
self.expandAll() self.expandAll()
# TODO: figure out if we can avoid this line in a better way
# such that "re-selection" doesn't happen tree-wise for each new
# sub-search:
# https://doc.qt.io/qt-5/model-view-programming.html#handling-selections-in-item-views
# XXX: THE BELOW LINE MUST BE CALLED. # XXX: THE BELOW LINE MUST BE CALLED.
# this stuff is super finicky and if not done right will cause # this stuff is super finicky and if not done right will cause
# Qt crashes out our buttz. it's required in order to get the # Qt crashes out our buttz. it's required in order to get the
@ -462,6 +427,10 @@ class SearchBar(QtWidgets.QLineEdit):
super().__init__(parent) super().__init__(parent)
# self.setContextMenuPolicy(Qt.CustomContextMenu)
# self.customContextMenuRequested.connect(self.show_menu)
# self.setStyleSheet(f"font: 18px")
self.view: CompleterView = view self.view: CompleterView = view
self.dpi_font = font self.dpi_font = font
self.chart_app = parent_chart self.chart_app = parent_chart
@ -477,10 +446,6 @@ class SearchBar(QtWidgets.QLineEdit):
# witty bit of margin # witty bit of margin
self.setTextMargins(2, 2, 2, 2) self.setTextMargins(2, 2, 2, 2)
# self.setContextMenuPolicy(Qt.CustomContextMenu)
# self.customContextMenuRequested.connect(self.show_menu)
# self.setStyleSheet(f"font: 18px")
def focus(self) -> None: def focus(self) -> None:
self.selectAll() self.selectAll()
self.show() self.show()
@ -508,12 +473,18 @@ class SearchBar(QtWidgets.QLineEdit):
class SearchWidget(QtGui.QWidget): class SearchWidget(QtGui.QWidget):
'''Composed widget of ``SearchBar`` + ``CompleterView``.
Includes helper methods for item management in the sub-widgets.
'''
def __init__( def __init__(
self, self,
chart_space: 'ChartSpace', # type: ignore # noqa chart_space: 'ChartSpace', # type: ignore # noqa
columns: List[str] = ['src', 'symbol'], columns: List[str] = ['src', 'symbol'],
parent=None, parent=None,
):
) -> None:
super().__init__(parent or chart_space) super().__init__(parent or chart_space)
# size it as we specify # size it as we specify
@ -529,10 +500,11 @@ class SearchWidget(QtGui.QWidget):
self.vbox.setSpacing(4) self.vbox.setSpacing(4)
# split layout for the (label:| search bar entry) # split layout for the (label:| search bar entry)
self.bar_hbox = QtGui.QHBoxLayout(self) self.bar_hbox = QtGui.QHBoxLayout()
self.bar_hbox.setContentsMargins(0, 0, 0, 0) self.bar_hbox.setContentsMargins(0, 0, 0, 0)
self.bar_hbox.setSpacing(4) self.bar_hbox.setSpacing(4)
# add label to left of search bar
self.label = label = QtGui.QLabel(parent=self) self.label = label = QtGui.QLabel(parent=self)
label.setTextFormat(3) # markdown label.setTextFormat(3) # markdown
label.setFont(_font.font) label.setFont(_font.font)
@ -546,9 +518,6 @@ class SearchWidget(QtGui.QWidget):
self.bar_hbox.addWidget(label) self.bar_hbox.addWidget(label)
# https://doc.qt.io/qt-5/qlayout.html#SizeConstraint-enum
# self.vbox.setSizeConstraint(QLayout.SetMaximumSize)
self.view = CompleterView( self.view = CompleterView(
parent=self, parent=self,
labels=columns, labels=columns,
@ -560,8 +529,6 @@ class SearchWidget(QtGui.QWidget):
) )
self.bar_hbox.addWidget(self.bar) self.bar_hbox.addWidget(self.bar)
# self.vbox.addWidget(self.bar)
# self.vbox.setAlignment(self.bar, Qt.AlignTop | Qt.AlignRight)
self.vbox.addLayout(self.bar_hbox) self.vbox.addLayout(self.bar_hbox)
self.vbox.setAlignment(self.bar, Qt.AlignTop | Qt.AlignRight) self.vbox.setAlignment(self.bar, Qt.AlignTop | Qt.AlignRight)
@ -595,6 +562,7 @@ class SearchWidget(QtGui.QWidget):
# to figure out the desired field(s) # to figure out the desired field(s)
# https://doc.qt.io/qt-5/qstandarditemmodel.html#itemFromIndex # https://doc.qt.io/qt-5/qstandarditemmodel.html#itemFromIndex
node = model.itemFromIndex(cidx.siblingAtColumn(1)) node = model.itemFromIndex(cidx.siblingAtColumn(1))
if node: if node:
symbol = node.text() symbol = node.text()
try: try:
@ -612,6 +580,52 @@ class SearchWidget(QtGui.QWidget):
else: else:
return None return None
def chart_current_item(
self,
clear_to_cache: bool = True,
) -> Optional[str]:
'''Attempt to load and switch the current selected
completion result to the affiliated chart app.
Return any loaded symbol
'''
value = self.get_current_item()
if value is None:
return None
provider, symbol = value
chart = self.chart_app
log.info(f'Requesting symbol: {symbol}.{provider}')
chart.load_symbol(
provider,
symbol,
'info',
)
# fully qualified symbol name (SNS i guess is what we're
# making?)
fqsn = '.'.join([symbol, provider]).lower()
# Re-order the symbol cache on the chart to display in
# LIFO order. this is normally only done internally by
# the chart on new symbols being loaded into memory
chart.set_chart_symbol(fqsn, chart.linkedcharts)
if clear_to_cache:
self.bar.clear()
self.view.set_section_entries(
'cache',
values=list(reversed(chart._chart_cache)),
# remove all other completion results except for cache
clear_all=True,
)
return fqsn
_search_active: trio.Event = trio.Event() _search_active: trio.Event = trio.Event()
_search_enabled: bool = False _search_enabled: bool = False
@ -630,7 +644,9 @@ async def pack_matches(
) -> None: ) -> None:
log.info(f'Searching {provider} for "{pattern}"') log.info(f'Searching {provider} for "{pattern}"')
if provider != 'cache': if provider != 'cache':
# insert provider entries with search status
view.set_section_entries( view.set_section_entries(
section=provider, section=provider,
values=[], values=[],
@ -645,13 +661,14 @@ async def pack_matches(
# ensure ^ status is updated # ensure ^ status is updated
results = await search(pattern) results = await search(pattern)
if provider != 'cache': if provider != 'cache': # XXX: don't cache the cache results xD
matches[(provider, pattern)] = results matches[(provider, pattern)] = results
# print(f'results from {provider}: {results}') # print(f'results from {provider}: {results}')
has_results[pattern].add(provider) has_results[pattern].add(provider)
if results: if results:
# display completion results
view.set_section_entries( view.set_section_entries(
section=provider, section=provider,
values=results, values=results,
@ -661,10 +678,9 @@ async def pack_matches(
async def fill_results( async def fill_results(
search: SearchBar, search: SearchBar,
# multisearch: Callable[..., Awaitable],
recv_chan: trio.abc.ReceiveChannel, recv_chan: trio.abc.ReceiveChannel,
# kb debouncing pauses # kb debouncing pauses (bracket defaults)
min_pause_time: float = 0.0616, min_pause_time: float = 0.0616,
max_pause_time: float = 6/16, max_pause_time: float = 6/16,
@ -736,7 +752,9 @@ async def fill_results(
# "searching.." statuses on outstanding results providers # "searching.." statuses on outstanding results providers
async with trio.open_nursery() as n: async with trio.open_nursery() as n:
for provider, (search, pause) in _searcher_cache.copy().items(): for provider, (search, pause) in (
_searcher_cache.copy().items()
):
if provider != 'cache': if provider != 'cache':
view.clear_section( view.clear_section(
@ -766,8 +784,8 @@ async def fill_results(
else: else:
view.clear_section(provider) view.clear_section(provider)
# if last_patt is None or last_patt != text: if last_patt is None or last_patt != text:
# view.select_first() view.select_first()
last_patt = text last_patt = text
bar.show() bar.show()
@ -777,7 +795,6 @@ async def handle_keyboard_input(
search: SearchWidget, search: SearchWidget,
recv_chan: trio.abc.ReceiveChannel, recv_chan: trio.abc.ReceiveChannel,
keyboard_pause_period: float = 0.0616,
) -> None: ) -> None:
@ -819,36 +836,7 @@ async def handle_keyboard_input(
if key in (Qt.Key_Enter, Qt.Key_Return): if key in (Qt.Key_Enter, Qt.Key_Return):
value = search.get_current_item() search.chart_current_item(clear_to_cache=True)
if value is None:
continue
provider, symbol = value
log.info(f'Requesting symbol: {symbol}.{provider}')
chart.load_symbol(
provider,
symbol,
'info',
)
# fully qualified symbol name (SNS i guess is what we're
# making?)
fqsn = '.'.join([symbol, provider]).lower()
# Re-order the symbol cache on the chart to display in
# LIFO order. this is normally only done internally by
# the chart on new symbols being loaded into memory
chart.set_chart_symbol(fqsn, chart.linkedcharts)
search.bar.clear()
view.set_section_entries(
'cache',
values=list(reversed(chart._chart_cache)),
clear_all=True,
)
_search_enabled = False _search_enabled = False
continue continue
@ -893,12 +881,12 @@ async def handle_keyboard_input(
view.next_section(direction='up') view.next_section(direction='up')
# selection navigation controls # selection navigation controls
elif ctl and key in { elif (ctl and key in {
Qt.Key_K, Qt.Key_K,
Qt.Key_J, Qt.Key_J,
} or key in { }) or key in {
Qt.Key_Up, Qt.Key_Up,
Qt.Key_Down, Qt.Key_Down,