# piker: trading gear for hackers # Copyright (C) Tyler Goodlet (in stewardship for pikers) # 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 . ''' Text entry "forms" widgets (mostly for configuration and UI user input). ''' from __future__ import annotations from contextlib import asynccontextmanager from functools import partial from textwrap import dedent from typing import ( Optional, Any, Callable, Awaitable ) import trio from PyQt5 import QtGui from PyQt5.QtCore import QSize, QModelIndex, Qt, QEvent from PyQt5.QtWidgets import ( QWidget, QLabel, QComboBox, QLineEdit, QHBoxLayout, QVBoxLayout, QFormLayout, QProgressBar, QSizePolicy, QStyledItemDelegate, QStyleOptionViewItem, ) # import pydantic from ._event import open_handlers from ._style import hcolor, _font, _font_small, DpiAwareFont from ._label import FormatLabel from .. import config class FontAndChartAwareLineEdit(QLineEdit): def __init__( self, parent: QWidget, # parent_chart: QWidget, # noqa font: DpiAwareFont = _font, width_in_chars: int = None, ) -> None: # self.setContextMenuPolicy(Qt.CustomContextMenu) # self.customContextMenuRequested.connect(self.show_menu) # self.setStyleSheet(f"font: 18px") self.dpi_font = font # self.godwidget = parent_chart if width_in_chars: self._chars = int(width_in_chars) else: # chart count which will be used to calculate # width of input field. self._chars: int = 9 super().__init__(parent) # size it as we specify # https://doc.qt.io/qt-5/qsizepolicy.html#Policy-enum self.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed, ) self.setFont(font.font) # witty bit of margin self.setTextMargins(2, 2, 2, 2) def sizeHint(self) -> QSize: """ Scale edit box to size of dpi aware font. """ psh = super().sizeHint() dpi_font = self.dpi_font psh.setHeight(dpi_font.px_size) # space for ``._chars: int`` char_w_pxs = dpi_font.boundingRect(self.text()).width() chars_w = char_w_pxs + 6 # * dpi_font.scale() * self._chars psh.setWidth(chars_w) return psh def set_width_in_chars( self, chars: int, ) -> None: self._chars = chars self.sizeHint() self.update() def focus(self) -> None: self.selectAll() self.show() self.setFocus() class FontScaledDelegate(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, option: QStyleOptionViewItem, index: QModelIndex, ) -> QSize: # value = index.data() # br = self.dpi_font.boundingRect(value) # w, h = br.width(), br.height() parent = self.parent() if getattr(parent, '_max_item_size', None): return QSize(*self.parent()._max_item_size) else: return super().sizeHint(option, index) # NOTE: hack to display icons on RHS # TODO: is there a way to set this stype option once? # def paint(self, painter, option, index): # # display icons on RHS # # https://stackoverflow.com/a/39943629 # option.decorationPosition = QtGui.QStyleOptionViewItem.Right # option.decorationAlignment = Qt.AlignRight | Qt.AlignVCenter # QStyledItemDelegate.paint(self, painter, option, index) # NOTE: in theory we can put icons on the RHS side with this hackery: # https://stackoverflow.com/a/64256969 # class ComboBox(QComboBox): # def __init__( # self, # parent=None, # ) -> None: # super().__init__(parent=parent) # def showPopup(self): # print('show') # QComboBox.showPopup(self) # def hidePopup(self): # # self.setItemDelegate(FontScaledDelegate(self.parent())) # print('hide') # QComboBox.hidePopup(self) # slew of resources which helped get this where it is: # https://stackoverflow.com/questions/20648210/qcombobox-adjusttocontents-changing-height # https://stackoverflow.com/questions/3151798/how-do-i-set-the-qcombobox-width-to-fit-the-largest-item # https://stackoverflow.com/questions/6337589/qlistwidget-adjust-size-to-content#6370892 # https://stackoverflow.com/questions/25304267/qt-resize-of-qlistview # https://stackoverflow.com/questions/28227406/how-to-set-qlistview-rows-height-permanently class FieldsForm(QWidget): vbox: QVBoxLayout form: QFormLayout def __init__( self, parent=None, ) -> None: super().__init__(parent) # size it as we specify self.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding, ) # XXX: not sure why we have to create this here exactly # (instead of in the pane creation routine) but it's # here and is managed by downstream layout routines. # best guess is that you have to create layouts in order # of hierarchy in order for things to display correctly? # TODO: we may want to hand this *down* from some "pane manager" # thing eventually? self.vbox = QVBoxLayout(self) # self.vbox.setAlignment(Qt.AlignVCenter) self.vbox.setAlignment(Qt.AlignBottom) self.vbox.setContentsMargins(0, 4, 3, 6) self.vbox.setSpacing(0) # split layout for the (