Initial WIP search completer; still a mess
parent
c26f4d9877
commit
0e83906f11
|
@ -24,7 +24,6 @@ from types import ModuleType
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui
|
from PyQt5 import QtCore, QtGui
|
||||||
from PyQt5 import QtWidgets
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
import tractor
|
import tractor
|
||||||
|
@ -48,7 +47,6 @@ from ._graphics._ohlc import BarItems
|
||||||
from ._graphics._curve import FastAppendCurve
|
from ._graphics._curve import FastAppendCurve
|
||||||
from ._style import (
|
from ._style import (
|
||||||
_font,
|
_font,
|
||||||
DpiAwareFont,
|
|
||||||
hcolor,
|
hcolor,
|
||||||
CHART_MARGINS,
|
CHART_MARGINS,
|
||||||
_xaxis_at,
|
_xaxis_at,
|
||||||
|
@ -56,6 +54,7 @@ from ._style import (
|
||||||
_bars_from_right_in_follow_mode,
|
_bars_from_right_in_follow_mode,
|
||||||
_bars_to_left_in_follow_mode,
|
_bars_to_left_in_follow_mode,
|
||||||
)
|
)
|
||||||
|
from ._search import FontSizedQLineEdit
|
||||||
from ..data._source import Symbol
|
from ..data._source import Symbol
|
||||||
from ..data._sharedmem import ShmArray
|
from ..data._sharedmem import ShmArray
|
||||||
from .. import brokers
|
from .. import brokers
|
||||||
|
@ -71,99 +70,6 @@ from .. import fsp
|
||||||
log = get_logger(__name__)
|
log = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FontSizedQLineEdit(QtWidgets.QLineEdit):
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
parent_chart: 'ChartSpace',
|
|
||||||
font: DpiAwareFont = _font,
|
|
||||||
) -> None:
|
|
||||||
super().__init__(parent_chart)
|
|
||||||
|
|
||||||
self.dpi_font = font
|
|
||||||
self.chart_app = parent_chart
|
|
||||||
|
|
||||||
# size it as we specify
|
|
||||||
self.setSizePolicy(
|
|
||||||
QtWidgets.QSizePolicy.Fixed,
|
|
||||||
QtWidgets.QSizePolicy.Fixed,
|
|
||||||
)
|
|
||||||
self.setFont(font.font)
|
|
||||||
|
|
||||||
# witty bit of margin
|
|
||||||
self.setTextMargins(2, 2, 2, 2)
|
|
||||||
|
|
||||||
def sizeHint(self) -> QtCore.QSize:
|
|
||||||
psh = super().sizeHint()
|
|
||||||
psh.setHeight(self.dpi_font.px_size + 2)
|
|
||||||
return psh
|
|
||||||
|
|
||||||
def unfocus(self) -> None:
|
|
||||||
self.hide()
|
|
||||||
self.clearFocus()
|
|
||||||
|
|
||||||
def keyPressEvent(self, ev: QtCore.QEvent) -> None:
|
|
||||||
# by default we don't markt it as consumed?
|
|
||||||
# ev.ignore()
|
|
||||||
super().keyPressEvent(ev)
|
|
||||||
|
|
||||||
ev.accept()
|
|
||||||
# text = ev.text()
|
|
||||||
key = ev.key()
|
|
||||||
mods = ev.modifiers()
|
|
||||||
|
|
||||||
ctrl = False
|
|
||||||
if mods == QtCore.Qt.ControlModifier:
|
|
||||||
ctrl = True
|
|
||||||
|
|
||||||
if ctrl:
|
|
||||||
if key == QtCore.Qt.Key_C:
|
|
||||||
self.unfocus()
|
|
||||||
self.chart_app.linkedcharts.focus()
|
|
||||||
|
|
||||||
# TODO:
|
|
||||||
elif key == QtCore.Qt.Key_K:
|
|
||||||
print('move up')
|
|
||||||
|
|
||||||
elif key == QtCore.Qt.Key_J:
|
|
||||||
print('move down')
|
|
||||||
|
|
||||||
elif key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
|
|
||||||
print(f'Requesting symbol: {self.text()}')
|
|
||||||
symbol = self.text()
|
|
||||||
app = self.chart_app
|
|
||||||
self.chart_app.load_symbol(
|
|
||||||
app.linkedcharts.symbol.brokers[0],
|
|
||||||
symbol,
|
|
||||||
'info',
|
|
||||||
)
|
|
||||||
# self.hide()
|
|
||||||
self.unfocus()
|
|
||||||
|
|
||||||
# if self._executing():
|
|
||||||
# # ignore all key presses while executing, except for Ctrl-C
|
|
||||||
# if event.modifiers() == Qt.ControlModifier and key == Qt.Key_C:
|
|
||||||
# self._handle_ctrl_c()
|
|
||||||
# return True
|
|
||||||
|
|
||||||
# handler = self._key_event_handlers.get(key)
|
|
||||||
# intercepted = handler and handler(event)
|
|
||||||
|
|
||||||
# Assumes that Control+Key is a movement command, i.e. should not be
|
|
||||||
# handled as text insertion. However, on win10 AltGr is reported as
|
|
||||||
# Alt+Control which is why we handle this case like regular
|
|
||||||
# # keypresses, see #53:
|
|
||||||
# if not event.modifiers() & Qt.ControlModifier or \
|
|
||||||
# event.modifiers() & Qt.AltModifier:
|
|
||||||
# self._keep_cursor_in_buffer()
|
|
||||||
|
|
||||||
# if not intercepted and event.text():
|
|
||||||
# intercepted = True
|
|
||||||
# self.insert_input_text(event.text())
|
|
||||||
|
|
||||||
# return False
|
|
||||||
|
|
||||||
|
|
||||||
class ChartSpace(QtGui.QWidget):
|
class ChartSpace(QtGui.QWidget):
|
||||||
"""High level widget which contains layouts for organizing
|
"""High level widget which contains layouts for organizing
|
||||||
lower level charts as well as other widgets used to control
|
lower level charts as well as other widgets used to control
|
||||||
|
@ -172,9 +78,9 @@ class ChartSpace(QtGui.QWidget):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self.v_layout = QtGui.QVBoxLayout(self)
|
self.vbox = QtGui.QVBoxLayout(self)
|
||||||
self.v_layout.setContentsMargins(0, 0, 0, 0)
|
self.vbox.setContentsMargins(0, 0, 0, 0)
|
||||||
self.v_layout.setSpacing(1)
|
self.vbox.setSpacing(2)
|
||||||
|
|
||||||
self.toolbar_layout = QtGui.QHBoxLayout()
|
self.toolbar_layout = QtGui.QHBoxLayout()
|
||||||
self.toolbar_layout.setContentsMargins(0, 0, 0, 0)
|
self.toolbar_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
@ -184,8 +90,8 @@ class ChartSpace(QtGui.QWidget):
|
||||||
|
|
||||||
# self.init_timeframes_ui()
|
# self.init_timeframes_ui()
|
||||||
# self.init_strategy_ui()
|
# self.init_strategy_ui()
|
||||||
self.v_layout.addLayout(self.toolbar_layout)
|
self.vbox.addLayout(self.toolbar_layout)
|
||||||
self.v_layout.addLayout(self.h_layout)
|
self.vbox.addLayout(self.h_layout)
|
||||||
self._chart_cache = {}
|
self._chart_cache = {}
|
||||||
self.linkedcharts: 'LinkedSplitCharts' = None
|
self.linkedcharts: 'LinkedSplitCharts' = None
|
||||||
self.symbol_label: Optional[QtGui.QLabel] = None
|
self.symbol_label: Optional[QtGui.QLabel] = None
|
||||||
|
@ -196,7 +102,16 @@ class ChartSpace(QtGui.QWidget):
|
||||||
# search = self.search = QtWidgets.QLineEdit()
|
# search = self.search = QtWidgets.QLineEdit()
|
||||||
self.search = FontSizedQLineEdit(self)
|
self.search = FontSizedQLineEdit(self)
|
||||||
self.search.unfocus()
|
self.search.unfocus()
|
||||||
self.v_layout.addWidget(self.search)
|
self.vbox.addWidget(self.search)
|
||||||
|
self.vbox.addWidget(self.search.view)
|
||||||
|
self.search.view.set_results([
|
||||||
|
'XMRUSD',
|
||||||
|
'XBTUSD',
|
||||||
|
'XMRXBT',
|
||||||
|
# 'XMRXBT',
|
||||||
|
# 'XDGUSD',
|
||||||
|
# 'ADAUSD',
|
||||||
|
])
|
||||||
|
|
||||||
# search.installEventFilter(self)
|
# search.installEventFilter(self)
|
||||||
|
|
||||||
|
@ -237,14 +152,13 @@ class ChartSpace(QtGui.QWidget):
|
||||||
"""
|
"""
|
||||||
linkedcharts = self._chart_cache.get(symbol_key)
|
linkedcharts = self._chart_cache.get(symbol_key)
|
||||||
|
|
||||||
# switching to a new viewable chart
|
if not self.vbox.isEmpty():
|
||||||
if not self.v_layout.isEmpty():
|
|
||||||
# XXX: this is CRITICAL especially with pixel buffer caching
|
# XXX: this is CRITICAL especially with pixel buffer caching
|
||||||
self.linkedcharts.hide()
|
self.linkedcharts.hide()
|
||||||
|
|
||||||
# XXX: pretty sure we don't need this
|
# XXX: pretty sure we don't need this
|
||||||
# remove any existing plots?
|
# remove any existing plots?
|
||||||
# self.v_layout.removeWidget(self.linkedcharts)
|
# self.vbox.removeWidget(self.linkedcharts)
|
||||||
|
|
||||||
# switching to a new viewable chart
|
# switching to a new viewable chart
|
||||||
if linkedcharts is None or reset:
|
if linkedcharts is None or reset:
|
||||||
|
@ -258,14 +172,14 @@ class ChartSpace(QtGui.QWidget):
|
||||||
symbol_key,
|
symbol_key,
|
||||||
loglevel,
|
loglevel,
|
||||||
)
|
)
|
||||||
self.v_layout.addWidget(linkedcharts)
|
self.vbox.addWidget(linkedcharts)
|
||||||
self._chart_cache[symbol_key] = linkedcharts
|
self._chart_cache[symbol_key] = linkedcharts
|
||||||
|
|
||||||
# chart is already in memory so just focus it
|
# chart is already in memory so just focus it
|
||||||
if self.linkedcharts:
|
if self.linkedcharts:
|
||||||
self.linkedcharts.unfocus()
|
self.linkedcharts.unfocus()
|
||||||
|
|
||||||
# self.v_layout.addWidget(linkedcharts)
|
# self.vbox.addWidget(linkedcharts)
|
||||||
linkedcharts.show()
|
linkedcharts.show()
|
||||||
linkedcharts.focus()
|
linkedcharts.focus()
|
||||||
self.linkedcharts = linkedcharts
|
self.linkedcharts = linkedcharts
|
||||||
|
@ -1348,7 +1262,6 @@ async def run_fsp(
|
||||||
# data-array as first msg
|
# data-array as first msg
|
||||||
_ = await stream.receive()
|
_ = await stream.receive()
|
||||||
|
|
||||||
conf['stream'] = stream
|
|
||||||
conf['portal'] = portal
|
conf['portal'] = portal
|
||||||
|
|
||||||
shm = conf['shm']
|
shm = conf['shm']
|
||||||
|
@ -1414,8 +1327,6 @@ async def run_fsp(
|
||||||
|
|
||||||
chart._set_yrange()
|
chart._set_yrange()
|
||||||
|
|
||||||
stream = conf['stream']
|
|
||||||
|
|
||||||
last = time.time()
|
last = time.time()
|
||||||
|
|
||||||
# update chart graphics
|
# update chart graphics
|
||||||
|
@ -1676,7 +1587,6 @@ async def _async_main(
|
||||||
# configure global DPI aware font size
|
# configure global DPI aware font size
|
||||||
_font.configure_to_dpi(screen)
|
_font.configure_to_dpi(screen)
|
||||||
|
|
||||||
# try:
|
|
||||||
async with trio.open_nursery() as root_n:
|
async with trio.open_nursery() as root_n:
|
||||||
|
|
||||||
# set root nursery for spawning other charts/feeds
|
# set root nursery for spawning other charts/feeds
|
||||||
|
@ -1691,9 +1601,6 @@ async def _async_main(
|
||||||
|
|
||||||
await trio.sleep_forever()
|
await trio.sleep_forever()
|
||||||
|
|
||||||
# finally:
|
|
||||||
# root_n.cancel()
|
|
||||||
|
|
||||||
|
|
||||||
def _main(
|
def _main(
|
||||||
sym: str,
|
sym: str,
|
||||||
|
|
|
@ -0,0 +1,415 @@
|
||||||
|
# piker: trading gear for hackers
|
||||||
|
# Copyright (C) Tyler Goodlet (in stewardship for piker0)
|
||||||
|
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""
|
||||||
|
qompleterz: embeddable search and complete using trio, Qt and fuzzywuzzy.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
from PyQt5 import QtWidgets
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt,
|
||||||
|
QSize,
|
||||||
|
QModelIndex,
|
||||||
|
QItemSelectionModel,
|
||||||
|
# QStringListModel
|
||||||
|
)
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QStandardItem,
|
||||||
|
QStandardItemModel,
|
||||||
|
)
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QTreeView,
|
||||||
|
# QListWidgetItem,
|
||||||
|
QAbstractScrollArea,
|
||||||
|
QStyledItemDelegate,
|
||||||
|
)
|
||||||
|
from fuzzywuzzy import process
|
||||||
|
|
||||||
|
|
||||||
|
# from PyQt5.QtWidgets import QCompleter, QComboBox
|
||||||
|
|
||||||
|
from ..log import get_logger
|
||||||
|
from ._style import (
|
||||||
|
_font,
|
||||||
|
DpiAwareFont,
|
||||||
|
hcolor,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
log = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleDelegate(QStyledItemDelegate):
|
||||||
|
"""
|
||||||
|
Super simple view delegate to render text in the same
|
||||||
|
font size as the search widget.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
parent=None,
|
||||||
|
font: DpiAwareFont = _font,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(parent)
|
||||||
|
self.dpi_font = font
|
||||||
|
|
||||||
|
# def sizeHint(self, *args) -> QtCore.QSize:
|
||||||
|
# """
|
||||||
|
# Scale edit box to size of dpi aware font.
|
||||||
|
|
||||||
|
# """
|
||||||
|
# psh = super().sizeHint(*args)
|
||||||
|
# # psh.setHeight(self.dpi_font.px_size + 2)
|
||||||
|
|
||||||
|
# psh.setHeight(18)
|
||||||
|
# # psh.setHeight(18)
|
||||||
|
# return psh
|
||||||
|
|
||||||
|
|
||||||
|
class CompleterView(QTreeView):
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
parent=None,
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self._font_size: int = 0 # pixels
|
||||||
|
self._cache: Dict[str, List[str]] = {}
|
||||||
|
|
||||||
|
# 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 resize(self):
|
||||||
|
model = self.model()
|
||||||
|
cols = model.columnCount()
|
||||||
|
|
||||||
|
for i in range(cols):
|
||||||
|
self.resizeColumnToContents(i)
|
||||||
|
|
||||||
|
# inclusive of search bar and header "rows" in pixel terms
|
||||||
|
rows = model.rowCount() + 2
|
||||||
|
# max_rows = 8 # 6 + search and headers
|
||||||
|
row_px = self.rowHeight(self.currentIndex())
|
||||||
|
# print(f'font_h: {font_h}\n px_height: {px_height}')
|
||||||
|
|
||||||
|
self.setMinimumSize(self.width(), rows * row_px)
|
||||||
|
self.setMaximumSize(self.width(), rows * row_px)
|
||||||
|
|
||||||
|
def set_font_size(self, size: int = 18):
|
||||||
|
# dpi_px_size = _font.px_size
|
||||||
|
print(size)
|
||||||
|
if size < 0:
|
||||||
|
size = 16
|
||||||
|
|
||||||
|
self._font_size = size
|
||||||
|
|
||||||
|
self.setStyleSheet(f"font: {size}px")
|
||||||
|
|
||||||
|
def set_results(
|
||||||
|
self,
|
||||||
|
results: List[str],
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
model = self.model()
|
||||||
|
|
||||||
|
for i, s in enumerate(results):
|
||||||
|
|
||||||
|
ix = QStandardItem(str(i))
|
||||||
|
item = QStandardItem(s)
|
||||||
|
# item.setCheckable(False)
|
||||||
|
|
||||||
|
src = QStandardItem('kraken')
|
||||||
|
|
||||||
|
# Add the item to the model
|
||||||
|
model.appendRow([src, ix, item])
|
||||||
|
|
||||||
|
def find_matches(
|
||||||
|
self,
|
||||||
|
field: str,
|
||||||
|
txt: str,
|
||||||
|
) -> List[QStandardItem]:
|
||||||
|
model = self.model()
|
||||||
|
items = model.findItems(
|
||||||
|
txt,
|
||||||
|
Qt.MatchContains,
|
||||||
|
self.field_to_col(field),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def mk_completer_view(
|
||||||
|
|
||||||
|
labels: List[str],
|
||||||
|
|
||||||
|
) -> QTreeView:
|
||||||
|
|
||||||
|
tree = CompleterView()
|
||||||
|
model = QStandardItemModel(tree)
|
||||||
|
|
||||||
|
# a std "tabular" config
|
||||||
|
tree.setItemDelegate(SimpleDelegate())
|
||||||
|
tree.setModel(model)
|
||||||
|
tree.setAlternatingRowColors(True)
|
||||||
|
tree.setIndentation(1)
|
||||||
|
|
||||||
|
# tree.setUniformRowHeights(True)
|
||||||
|
# tree.setColumnWidth(0, 3)
|
||||||
|
|
||||||
|
# ux settings
|
||||||
|
tree.setItemsExpandable(True)
|
||||||
|
tree.setExpandsOnDoubleClick(False)
|
||||||
|
tree.setAnimated(False)
|
||||||
|
tree.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||||
|
|
||||||
|
tree.setSizeAdjustPolicy(QAbstractScrollArea.AdjustIgnored)
|
||||||
|
|
||||||
|
# column headers
|
||||||
|
model.setHorizontalHeaderLabels(labels)
|
||||||
|
|
||||||
|
return tree
|
||||||
|
|
||||||
|
|
||||||
|
class FontSizedQLineEdit(QtWidgets.QLineEdit):
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
parent_chart: 'ChartSpace', # noqa
|
||||||
|
view: Optional[CompleterView] = None,
|
||||||
|
font: DpiAwareFont = _font,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(parent_chart)
|
||||||
|
|
||||||
|
# vbox = self.vbox = QtGui.QVBoxLayout(self)
|
||||||
|
# vbox.addWidget(self)
|
||||||
|
# self.vbox.setContentsMargins(0, 0, 0, 0)
|
||||||
|
# self.vbox.setSpacing(2)
|
||||||
|
|
||||||
|
self._view: CompleterView = view
|
||||||
|
|
||||||
|
self.dpi_font = font
|
||||||
|
self.chart_app = parent_chart
|
||||||
|
|
||||||
|
# size it as we specify
|
||||||
|
self.setSizePolicy(
|
||||||
|
QtWidgets.QSizePolicy.Fixed,
|
||||||
|
QtWidgets.QSizePolicy.Fixed,
|
||||||
|
)
|
||||||
|
self.setFont(font.font)
|
||||||
|
|
||||||
|
# witty bit of margin
|
||||||
|
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:
|
||||||
|
self.clear()
|
||||||
|
self.show()
|
||||||
|
self.setFocus()
|
||||||
|
|
||||||
|
def show(self) -> None:
|
||||||
|
super().show()
|
||||||
|
self.show_matches()
|
||||||
|
# self.view.show()
|
||||||
|
# self.view.resize()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def view(self) -> CompleterView:
|
||||||
|
|
||||||
|
if self._view is None:
|
||||||
|
view = mk_completer_view(['src', 'i', 'symbol'])
|
||||||
|
|
||||||
|
# print('yo')
|
||||||
|
# self.chart_app.vbox.addWidget(view)
|
||||||
|
# self.vbox.addWidget(view)
|
||||||
|
|
||||||
|
self._view = view
|
||||||
|
|
||||||
|
return self._view
|
||||||
|
|
||||||
|
def show_matches(self):
|
||||||
|
view = self.view
|
||||||
|
view.set_font_size(self.dpi_font.px_size)
|
||||||
|
view.show()
|
||||||
|
# scale columns
|
||||||
|
view.resize()
|
||||||
|
|
||||||
|
def sizeHint(self) -> QtCore.QSize:
|
||||||
|
"""
|
||||||
|
Scale edit box to size of dpi aware font.
|
||||||
|
|
||||||
|
"""
|
||||||
|
psh = super().sizeHint()
|
||||||
|
psh.setHeight(self.dpi_font.px_size + 2)
|
||||||
|
# psh.setHeight(12)
|
||||||
|
return psh
|
||||||
|
|
||||||
|
def unfocus(self) -> None:
|
||||||
|
self.hide()
|
||||||
|
self.clearFocus()
|
||||||
|
|
||||||
|
if self.view:
|
||||||
|
self.view.hide()
|
||||||
|
|
||||||
|
def keyPressEvent(self, ev: QtCore.QEvent) -> None:
|
||||||
|
# by default we don't markt it as consumed?
|
||||||
|
# ev.ignore()
|
||||||
|
super().keyPressEvent(ev)
|
||||||
|
|
||||||
|
ev.accept()
|
||||||
|
# text = ev.text()
|
||||||
|
key = ev.key()
|
||||||
|
mods = ev.modifiers()
|
||||||
|
txt = self.text()
|
||||||
|
|
||||||
|
if key in (Qt.Key_Enter, Qt.Key_Return):
|
||||||
|
|
||||||
|
print(f'Requesting symbol: {self.text()}')
|
||||||
|
|
||||||
|
# TODO: ensure there is a matching completion or error and
|
||||||
|
# do nothing
|
||||||
|
symbol = txt
|
||||||
|
|
||||||
|
app = self.chart_app
|
||||||
|
self.chart_app.load_symbol(
|
||||||
|
app.linkedcharts.symbol.brokers[0],
|
||||||
|
symbol,
|
||||||
|
'info',
|
||||||
|
)
|
||||||
|
self.unfocus()
|
||||||
|
return
|
||||||
|
|
||||||
|
ctrl = False
|
||||||
|
if mods == Qt.ControlModifier:
|
||||||
|
ctrl = True
|
||||||
|
|
||||||
|
view = self.view
|
||||||
|
model = view.model()
|
||||||
|
nidx = cidx = view.currentIndex()
|
||||||
|
sel = view.selectionModel()
|
||||||
|
# sel.clear()
|
||||||
|
|
||||||
|
# selection tips:
|
||||||
|
# - get parent: self.index(row, 0)
|
||||||
|
# - first item index: index = self.index(0, 0, parent)
|
||||||
|
|
||||||
|
if ctrl:
|
||||||
|
# we're in select mode or cancelling
|
||||||
|
|
||||||
|
if key == Qt.Key_C:
|
||||||
|
self.unfocus()
|
||||||
|
|
||||||
|
# kill the search and focus back on main chart
|
||||||
|
if self.chart_app:
|
||||||
|
self.chart_app.linkedcharts.focus()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# result selection nav
|
||||||
|
if key in (Qt.Key_K, Qt.Key_J):
|
||||||
|
|
||||||
|
if key == Qt.Key_K:
|
||||||
|
# self.view.setFocus()
|
||||||
|
nidx = view.indexAbove(cidx)
|
||||||
|
print('move up')
|
||||||
|
|
||||||
|
elif key == Qt.Key_J:
|
||||||
|
# self.view.setFocus()
|
||||||
|
nidx = view.indexBelow(cidx)
|
||||||
|
print('move down')
|
||||||
|
|
||||||
|
# select row without selecting.. :eye_rollzz:
|
||||||
|
# https://doc.qt.io/qt-5/qabstractitemview.html#setCurrentIndex
|
||||||
|
if nidx.isValid():
|
||||||
|
sel.setCurrentIndex(
|
||||||
|
nidx,
|
||||||
|
QItemSelectionModel.ClearAndSelect |
|
||||||
|
QItemSelectionModel.Rows
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: make this not hard coded to 2
|
||||||
|
# and use the ``CompleterView`` schema/settings
|
||||||
|
# to figure out the desired field(s)
|
||||||
|
value = model.item(nidx.row(), 2).text()
|
||||||
|
print(f'value: {value}')
|
||||||
|
self.setText(value)
|
||||||
|
|
||||||
|
else:
|
||||||
|
sel.setCurrentIndex(
|
||||||
|
model.index(0, 0, QModelIndex()),
|
||||||
|
QItemSelectionModel.ClearAndSelect | # type: ignore[arg-type]
|
||||||
|
QItemSelectionModel.Rows
|
||||||
|
)
|
||||||
|
|
||||||
|
self.show_matches()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
# local testing of **just** the search UI
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
|
||||||
|
syms = [
|
||||||
|
'XMRUSD',
|
||||||
|
'XBTUSD',
|
||||||
|
'XMRXBT',
|
||||||
|
'XDGUSD',
|
||||||
|
'ADAUSD',
|
||||||
|
]
|
||||||
|
|
||||||
|
# results.setFocusPolicy(Qt.NoFocus)
|
||||||
|
search = FontSizedQLineEdit(None, view=view)
|
||||||
|
search.view.set_results(syms)
|
||||||
|
|
||||||
|
# make a root widget to tie shit together
|
||||||
|
class W(QtGui.QWidget):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
vbox = self.vbox = QtGui.QVBoxLayout(self)
|
||||||
|
self.vbox.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.vbox.setSpacing(2)
|
||||||
|
|
||||||
|
main = W()
|
||||||
|
main.vbox.addWidget(search)
|
||||||
|
main.vbox.addWidget(view)
|
||||||
|
# main.show()
|
||||||
|
search.show()
|
||||||
|
|
||||||
|
sys.exit(app.exec_())
|
Loading…
Reference in New Issue