Module scrilla.gui.widgets.components
A series of classes that all inherit from `PySide6.QtWidgets.QWidget
and build up in sequence the necessary functionality to grab and validate user input, calculate and display results, etc. These widgets are not directly displayed on the GUI; rather, they are used as building blocks by the scrilla.gui.functions
module, to create widgets specifically for application functions.
All widgets have a similar structure in that they call a series of methods tailored to their specific class: 1. _init Child widgets are constructed. Layouts are created. 2. _arrange Child widgets are arranged. Layouts are applied. 3. _stage Child widgets are prepped for display.
The idea behind the module is to hide as much widget initialization and styling as possible behind its functions, so the scrilla.gui.functions
module can focus on application-level logic and isn't cluttered with ugly declarative configuration.
Widgets have styles applied to them through the scrilla.styles.app.qss
stylesheet. Widgets are layered over one another in a hierarchy described by: root
-> child
-> grand-child
-> great-grand-child
-> etc. Each layers has a theme that descends down the material color scheme hex codes in sequence with its place in the layer hierarchy. See scrilla.gui.formats
for more information.
Expand source code
# This file is part of scrilla: https://github.com/chinchalinchin/scrilla.
# scrilla is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3
# as published by the Free Software Foundation.
# scrilla 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 General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with scrilla. If not, see <https://www.gnu.org/licenses/>
# or <https://github.com/chinchalinchin/scrilla/blob/develop/main/LICENSE>.
"""
A series of classes that all inherit from ``PySide6.QtWidgets.QWidget` and build up in sequence the necessary functionality to grab and validate user input, calculate and display results, etc. These widgets are not directly displayed on the GUI; rather, they are used as building blocks by the `scrilla.gui.functions` module, to create widgets specifically for application functions.
All widgets have a similar structure in that they call a series of methods tailored to their specific class:
1. **_init**
Child widgets are constructed. Layouts are created.
2. **_arrange**
Child widgets are arranged. Layouts are applied.
3. **_stage**
Child widgets are prepped for display.
The idea behind the module is to hide as much widget initialization and styling as possible behind its functions, so the `scrilla.gui.functions` module can focus on application-level logic and isn't cluttered with ugly declarative configuration.
Widgets have styles applied to them through the `scrilla.styles.app.qss` stylesheet. Widgets are layered over one another in a hierarchy described by: `root` -> `child` -> `grand-child` -> `great-grand-child` -> etc. Each layers has a theme that descends down the material color scheme hex codes in sequence with its place in the layer hierarchy. See `scrilla.gui.formats` for more information.
"""
import datetime
from typing import Callable, Dict, List, Union
from PySide6 import QtCore, QtWidgets, QtGui
from scrilla import settings
from scrilla.util import helper
from scrilla.gui import utilities
from scrilla.static import definitions
from scrilla.gui.widgets import factories
SYMBOLS_LIST = "list"
"""Constant passed into `scrilla.gui.widgets.components.ArgumentWidget` to initialize an input control allowing user to input list of ticker symbols"""
SYMBOLS_SINGLE = "single"
"""Constant passed into `scrilla.gui.widgets.components.ArgumentWidget` to initialize an input control allowing user to input single ticker symbol"""
SYMBOLS_NONE = "none"
"""Constant passed into `scrilla.gui.widgets.components.ArgumentWidget` to initialize an input control without ticker symbols"""
class SkeletonWidget(QtWidgets.QWidget):
def __init__(self, function: str, parent: QtWidgets.QWidget):
super(SkeletonWidget, self).__init__(parent)
self._configure_control_skeleton(function)
def _configure_control_skeleton(self, function: str):
self.controls = factories.generate_control_skeleton()
for arg in definitions.FUNC_DICT[function]['args']:
if not definitions.ARG_DICT[arg]['cli_only']:
self.controls[arg] = True
class ArgumentWidget(QtWidgets.QWidget):
"""
Base class for other more complex widgets to inherit. An instance of `scrilla.gui.widgets.ArgumentWidget` embeds its child widgets in its own layout, which is an instance of``PySide6.QtWidgetQVBoxLayout``. This class provides access to the following widgets:a ``PySide6.QtWidgets.QLabel`` for the title widget, an error message widget, a ``PySide6.QtWidgets.QPushButton`` for the calculate button widget, a ``PySide6.QtWidget.QLineEdit`` for ticker symbol input, and a variety of optional input widgets enabled by boolean flags in the `controls` constructor argument.
Constructor
-----------
1. **calculate_function**: ``str``
Function attached to the `calculate_button` widget. Triggered when the widget is clicked.
2. **clear_function**: ``str``
Function attached to the `clear_button` widget. Trigged when the widget is clicked.
3. **controls:**: ``Dict[bool]``
Dictionary of boolean flags instructing the class which optional input to include. Optional keys can be found in `scrilla.static.definitions.ARG_DICT`. An dictionary skeleton with all the optional input disabled can be retrieved through `scrilla.gui.widgets.factories.generate_control_skeleton`.
4. **layer**: ``str``
Stylesheet property attached to widget.
5. **single**: ``bool``
Flag to limit symbol input to one ticker symbol. Calls to `get_symbol_input()` will return
Attributes
----------
1. **title**: ``PySide6.QtWidgets.QLabel``
2. **required_title**: ``PySide6.QtWidgets.QLabel``
3. **optional_title**: ``PySide6.QtWidgets.QLabel``
4. **required_pane**: ``PySide6.QtWidgets.QLabel``
5. **optional_pane**: ``PySide6.QtWidgets.QLabel``
6. **message**: ``PySide6.QtWidgets.QLabel``
Label attached to `self.symbol_input`.
7. **error_message**: ``PySide6.QtWidget.QLabel``
Message displayed if there is an error thrown during calculation.
8. **calculate_button**: ``PySide6.QtWidget.QPushButton``
Empty button for super classes to inherit, primed to fire events on return presses
9. **clear_button**: ``PySide6.QtWidget.QPushButton``
Empty button for super classes to inherit, primed to fire events on return presses.
10. **symbol_widget**: ``PySide6.QtWidget.QLineEdit``
Empty text area for super classes to inherit
11. **control_widgets**: ``Dict[str,Union[PySide6.QtWidgets.QWidget,None]]
Dictionary containing the optional input widgets.
"""
# TODO: calculate and clear should be part of THIS constructor, doesn't make sense to have other widgets hook them up.
def __init__(self, calculate_function: Callable, clear_function: Callable, controls: Dict[str, bool], layer, mode: str = SYMBOLS_LIST):
super().__init__()
self.layer = layer
self.controls = controls
self.control_widgets = {}
self.group_control_widgets = None
self.calculate_function = calculate_function
self.clear_function = clear_function
self._init_widgets(mode)
self._generate_group_widgets()
self._arrange_widgets()
self._stage_widgets()
def _init_widgets(self, mode: str) -> None:
"""
Creates child widgets by calling factory methods from `scrilla.gui.widgets.factories`. This method will iterate over `self.controls` and initialize the optional input widget accordingly. `self.optional_pane` and `self.required_pane`, the container widgets for the input elements, are initialized with a style tag on the same layer as the `scrilla.gui.widgets.components.ArgumentWidget`.
"""
self.title = factories.atomic_widget_factory(
component='subtitle', title='Function Input')
self.optional_title = factories.atomic_widget_factory(
component='label', title='Optional Arguments')
self.error_message = factories.atomic_widget_factory(
component='error', title="Error Message Goes Here")
self.calculate_button = factories.atomic_widget_factory(
component='calculate-button', title='Calculate')
self.clear_button = factories.atomic_widget_factory(
component='clear-button', title='Clear')
if mode == SYMBOLS_LIST:
self.required_title = factories.atomic_widget_factory(
component='label', title='Required Arguments')
self.symbol_hint = factories.atomic_widget_factory(
component='text', title="Separate Tickers With Commas")
self.required_pane = factories.layout_factory(
layout='vertical-box')
self.required_pane.setObjectName(self.layer)
self.symbol_widget = factories.argument_widget_factory(
component='symbols', title="Asset Tickers :", optional=False)
elif mode == SYMBOLS_SINGLE:
self.required_title = factories.atomic_widget_factory(
component='label', title='Required Argument')
self.symbol_hint = factories.atomic_widget_factory(
component='text', title="Enter a Single Symbol")
self.required_pane = factories.layout_factory(
layout='vertical-box')
self.required_pane.setObjectName(self.layer)
self.symbol_widget = factories.argument_widget_factory(
component='symbol', title="Symbol: ", optional=False)
else:
self.symbol_widget = None
self.group_definitions = None
for control in self.controls:
if self.controls[control]:
if self.controls[control]:
if definitions.ARG_DICT[control]["widget_type"] != "group":
self.control_widgets[control] = factories.argument_widget_factory(definitions.ARG_DICT[control]['widget_type'],
f'{definitions.ARG_DICT[control]["name"]} :',
optional=True)
else:
if self.group_definitions is None:
self.group_definitions = {}
self.group_definitions[definitions.ARG_DICT[control]
['name']] = definitions.ARG_DICT[control]
else:
self.control_widgets[control] = None
self.optional_pane = factories.layout_factory(layout='vertical-box')
self.optional_pane.setObjectName(self.layer)
self.setLayout(QtWidgets.QVBoxLayout())
def _generate_group_widgets(self):
if self.group_definitions is not None:
self.group_control_widgets = {}
groups = {}
for definition in self.group_definitions:
group_key = self.group_definitions[definition]['group']
group_name = definitions.GROUP_DICT[group_key]
if group_name not in groups.keys():
groups[group_name] = [definition]
else:
groups[group_name].append(definition)
for group_name, group in groups.items():
self.group_control_widgets[group_name] = factories.group_widget_factory(
group, group_name)
def _arrange_widgets(self):
"""
Arrange child widgets in their layouts and provides rendering hints. The `self.symbol_widget` is set into a ``PySide6.QtWidgets.QVBoxLayout`` named `self.required_pane`. The optional input widgets are set into a separate ``PySide6.QtWidgets.QVBoxLayout`` named `self.optional_pane`. `self.required_pane` and `self.optional_pane` are in turn set into a parent `PySide6.QtWidgets.QVBoxLayout``, along with `self.calculate_button` and `self.clear_button`. A strecth widget is inserted between the input widgets and the button widgets.
"""
factories.set_policy_on_widget_list([self.title, self.optional_title, self.optional_pane],
QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum))
if self.symbol_widget is not None:
factories.set_policy_on_widget_list([self.symbol_hint, self.required_title, self.required_pane],
QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum))
self.setSizePolicy(QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding))
if self.symbol_widget is not None:
self.required_pane.layout().addWidget(self.required_title)
self.required_pane.layout().addWidget(self.symbol_hint)
self.required_pane.layout().addWidget(self.symbol_widget)
if self.group_control_widgets is not None:
for widget in self.group_control_widgets.values():
self.required_pane.layout().addWidget(widget)
self.optional_pane.layout().addWidget(self.optional_title)
for control_widget in self.control_widgets.values():
if control_widget is not None:
self.optional_pane.layout().addWidget(control_widget)
self.layout().addWidget(self.title)
self.layout().addWidget(self.error_message)
if self.symbol_widget is not None:
self.layout().addWidget(self.required_pane)
self.layout().addWidget(self.optional_pane)
self.layout().addStretch()
self.layout().addWidget(self.calculate_button)
self.layout().addWidget(self.clear_button)
def _stage_widgets(self):
"""
Prepares child widgets for display. `self.clear_function` and `self.calculate` function are hooked into the `clicked` signal emitted from `self.clear_button` and `self.calculate_button`, respectively.
"""
self.error_message.hide()
self.clear_button.clicked.connect(self.clear_function)
self.calculate_button.clicked.connect(self.calculate_function)
if self.symbol_widget is not None:
# NOTE: symbol widget is technically a layout in which the lineedit is abutted by a label, so need
# to pull the actual input element from the layout
self.symbol_widget.layout().itemAt(1).widget(
).returnPressed.connect(self.calculate_function)
def get_symbol_input(self) -> Union[List[str], None]:
"""
Returns the symbols inputted into the `PySide6.QtWidgets.QLineEdit` child of `symbol_widget`. If the `ArgumentWidget` has been initialized as a `scrilla.gui.widgets.components.SYMBOLS_LIST`, the method will return a list of inputted strings. If the `ArgumentWidget` has been initialied as a `scrilla.gui.widgets.components.SYMBOLS_SINGLE`, the method will return a list with a string as its single element. If the `ArgumentWidget` has been initialized as a `scrilla.gui.widgets.components.SYMBOLS_NONE`, this method will return `None`. In addition, in the first two cases, if no input has been entered, this method will return `None`.
"""
if self.symbol_widget is not None:
return helper.split_and_strip(self.symbol_widget.layout().itemAt(1).widget().text())
return None
def get_control_input(self, control_widget_key: str) -> Union[datetime.date, str, bool, None]:
"""
Get the value on the specified optional input widget. Optional keys are accessed through the keys of the `scrilla.static.definitions.ARG_DICT` dictionary.
If the widget is disabled or has been excluded altogether from the parent widget, i.e. a value of `False` was passed in through the constructor's `controls` arguments for that optional input widget, this method will return `None`.
"""
if self.control_widgets[control_widget_key] is None:
return None
widget = self.control_widgets[control_widget_key].layout().itemAt(
1).widget()
if not widget.isEnabled():
return None
if type(widget) is QtWidgets.QDateEdit:
return widget.date().toPython()
if type(widget) is QtWidgets.QLineEdit:
if type(widget.validator()) is QtGui.QIntValidator:
return int(widget.text())
if type(widget.validator()) in [QtGui.QDoubleValidator, QtGui.QRegularExpressionValidator]:
return float(widget.text())
return widget.text()
if type(widget) is QtWidgets.QRadioButton:
return widget.isChecked()
def prime(self) -> None:
"""
Enables user input on child widgets, except `clear_button` which is disabled.
"""
self.clear_button.hide()
self.calculate_button.show()
if self.symbol_widget is not None:
self.symbol_widget.layout().itemAt(1).widget().setEnabled(True)
self.symbol_widget.layout().itemAt(1).widget().clear()
for control in self.control_widgets:
if self.control_widgets[control] is not None:
if type(self.control_widgets[control].layout().itemAt(1).widget()) != QtWidgets.QRadioButton:
self.control_widgets[control].layout().itemAt(
1).widget().clear()
self.control_widgets[control].layout().itemAt(
2).widget().setEnabled(True)
self.control_widgets[control].layout().itemAt(
2).widget().setCheckState(QtCore.Qt.Unchecked)
self.control_widgets[control].layout().itemAt(
1).widget().setEnabled(False)
# NOTE: have to set Widget1 to disabled last since the signal on the Widget2 is connected
# through CheckState to the enabled stated of Widget1, i.e. flipping the CheckState on
# Widget2 switches the enabled state of Widget1.
def fire(self) -> None:
"""
Disables user input on child widgets, except `clear_button` which is enabled.
"""
self.clear_button.show()
self.calculate_button.hide()
if self.symbol_widget is not None:
self.symbol_widget.layout().itemAt(1).widget().setEnabled(False)
for control in self.control_widgets:
if self.control_widgets[control] is not None:
self.control_widgets[control].layout().itemAt(
1).widget().setEnabled(False)
self.control_widgets[control].layout().itemAt(
2).widget().setEnabled(False)
class TableWidget(QtWidgets.QWidget):
"""
Base class for other more complex widgets to inherit. An instance of `scrilla.gui.widgets.TableWidget` embeds its child widgets in its own layout, which is an instance of``PySide6.QtWidgetQVBoxLayout``. This class provides access to the following widgets: a ``PySide6.QtWidgets.QLabel`` for the title widget, an error message widget and a ``PySide6.QtWidgets.QTableWidget`` for results display.
Parameters
----------
1. **layer**: ``str``
Stylesheet property attached to widget.
2. **widget_title**: ``str``
*Optional*. Defaults to "Table Result". Title of the widget.
Attributes
----------
1. **title**: ``PySide6.QtWidget.QLabel``
2.. **table**: ``PySide6.QtWidget.QTableWidget``
"""
def __init__(self, layer, widget_title: str = "Table Result"):
super().__init__()
self.layer = layer
self._init_widgets(widget_title)
self._arrange_widgets()
self._stage_widgets()
def _init_widgets(self, widget_title: str) -> None:
"""Creates child widgets and their layouts"""
self.title_container = factories.layout_factory(
layout='horizontal-box')
self.title = factories.atomic_widget_factory(
component='heading', title=widget_title)
self.download_button = factories.atomic_widget_factory(
component='download-button', title=None)
self.table = factories.atomic_widget_factory(
component='table', title=None)
self.setLayout(QtWidgets.QVBoxLayout())
def _arrange_widgets(self) -> None:
factories.set_policy_on_widget_list([self, self.title, self.title_container],
QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Minimum))
self.title_container.layout().addWidget(self.title)
self.title_container.layout().addWidget(self.download_button)
self.layout().addWidget(self.title_container)
self.layout().addWidget(self.table, 1)
def _stage_widgets(self) -> None:
self.table.hide()
self.download_button.hide()
self.download_button.clicked.connect(self.show_file_dialog)
def init_table(self, rows: List[str], columns: List[str]) -> None:
"""
Initializes `table` for display. Number of rows and columns is determined by the length of the passed in lists.
Parameters
----------
1. **rows**: ``List[str]``
List containing the row headers.
2. **columns**: ``List[str]``
List containing the column headers.
"""
self.table.setRowCount(len(rows))
self.table.setColumnCount(len(columns))
self.table.setHorizontalHeaderLabels(columns)
self.table.setVerticalHeaderLabels(rows)
def show_table(self):
self.table.resizeColumnsToContents()
self.table.show()
self.download_button.show()
@QtCore.Slot()
def show_file_dialog(self) -> None:
file_path = factories.atomic_widget_factory(
component='save-dialog', title=f'(*.{settings.FILE_EXT})')
file_path.selectFile(f'table.{settings.FILE_EXT}')
filename = None
if file_path.exec_() == QtWidgets.QDialog.Accepted:
filename = file_path.selectedFiles()
if filename is not None and len(filename) > 0:
if settings.FILE_EXT == 'json':
utilities.download_table_to_json(self.table, filename[0])
class GraphWidget(QtWidgets.QWidget):
"""
Base class for other more complex widgets to inherit. An instance of `scrilla.gui.widgets.GraphWidget` embeds its child widgets in its own layout, which is an instance of``PySide6.QtWidgetQVBoxLayout``. This class provides access to the following widgets: a ``PySide6.QtWidgets.QLabel`` for the title widget, an error message widget and a ``PySide6.QtWidgets.QTableWidget`` for results display.
The graph to be displayed should be stored in the `scrilla.settings.TEMP_DIR` directory before calling the `show_pixmap` method on this class. It will load the graph from file and convert it into a ``PySide6.QtGui.QPixMap``.
Parameters
----------
1. **tmp_graph_key**: ``str``
The key of the file in the `scrilla.settings.TEMP_DIR` used to store the image of the graph.
2. **widget_title**: ``str``
*Optional*. Defaults to "Graph Results". Title of the widget.
Attributes
----------
1. **tmp_graph_key**: ``str``
2. **title**: ``PySide6.QtWidget.QLabel``
3. **figure**: ``PySide6.QtWidget.QLabel``
"""
def __init__(self, tmp_graph_key: str, layer: str, widget_title: str = "Graph Results"):
super().__init__()
self.layer = layer
self.tmp_graph_key = tmp_graph_key
self._init_widgets(widget_title)
self._arrange_widgets()
self._stage_widgets()
def _init_widgets(self, widget_title: str) -> None:
self.title_container = factories.layout_factory(
layout='horizontal-box')
self.title = factories.atomic_widget_factory(
component='heading', title=widget_title)
self.download_button = factories.atomic_widget_factory(
component='download-button', title=None)
self.figure = factories.atomic_widget_factory(
component='figure', title=None)
self.setLayout(QtWidgets.QVBoxLayout())
def _arrange_widgets(self) -> None:
factories.set_policy_on_widget_list([self, self.title, self.title_container],
QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Minimum))
self.title_container.layout().addWidget(self.title)
self.title_container.layout().addWidget(self.download_button)
self.layout().addWidget(self.title_container)
self.layout().addWidget(self.figure)
def _stage_widgets(self) -> None:
self.figure.hide()
self.download_button.hide()
self.download_button.clicked.connect(self.show_file_dialog)
def set_pixmap(self) -> None:
self.figure.setPixmap(utilities.generate_pixmap_from_temp(
self.width(), self.height(), self.tmp_graph_key))
self.figure.show()
self.download_button.show()
def clear(self):
self.figure.hide()
self.download_button.hide()
@QtCore.Slot()
def show_file_dialog(self) -> None:
file_path = factories.atomic_widget_factory(
component='save-dialog', title=f'(*.{settings.IMG_EXT})')
file_path.selectFile(f'{self.tmp_graph_key}.{settings.IMG_EXT}')
filename = None
if file_path.exec_() == QtWidgets.QDialog.Accepted:
filename = file_path.selectedFiles()
if filename is not None and len(filename) > 0:
utilities.download_tmp_to_file(self.tmp_graph_key, filename[0])
class CompositeWidget(QtWidgets.QWidget):
"""
Constructor
-----------
1. **tmp_graph_key**: ``str``
2. **widget_title**: ``str``
*Optional*. Defaults to "Results". Title of the widget.
3. **table_title**: ``Union[str,None]``
4. **graph_title**: ``Union[str,None]``
Attributes
----------
1. **title**: ``PySide6.QtWidgets.QLabel``
2. **table_widget**: ``scrilla.gui.widgets.TableWidget``
3. **graph_widget**: ``scrilla.gui.widgets.GraphWidget``
4. **tab_widget**: ``PySide6.QtWidget.QTabWidget``
"""
def __init__(self, tmp_graph_key: str, layer: str, widget_title: str = "Results",
table_title: Union[str, None] = None,
graph_title: Union[str, None] = None):
super().__init__()
self.setObjectName(layer)
self._init_widgets(widget_title=widget_title,
tmp_graph_key=tmp_graph_key)
self._arrange_widgets(graph_title=graph_title,
table_title=table_title)
def _init_widgets(self, widget_title: str, tmp_graph_key: str) -> None:
"""Creates child widgets and their layouts"""
self.title = factories.atomic_widget_factory(
component='subtitle', title=widget_title)
self.table_widget = TableWidget(layer=self.objectName())
self.graph_widget = GraphWidget(
tmp_graph_key=tmp_graph_key, layer=self.objectName())
self.tab_widget = QtWidgets.QTabWidget()
self.setLayout(QtWidgets.QVBoxLayout())
def _arrange_widgets(self, graph_title: str, table_title: str) -> None:
self.title.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Minimum))
self.tab_widget.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding))
self.title.setAlignment(QtCore.Qt.AlignLeft)
self.tab_widget.addTab(self.table_widget, table_title)
self.tab_widget.addTab(self.graph_widget, graph_title)
self.layout().addWidget(self.title)
self.layout().addWidget(self.tab_widget)
Global variables
var SYMBOLS_LIST
-
Constant passed into
ArgumentWidget
to initialize an input control allowing user to input list of ticker symbols var SYMBOLS_NONE
-
Constant passed into
ArgumentWidget
to initialize an input control without ticker symbols var SYMBOLS_SINGLE
-
Constant passed into
ArgumentWidget
to initialize an input control allowing user to input single ticker symbol
Classes
class ArgumentWidget (calculate_function: Callable, clear_function: Callable, controls: Dict[str, bool], layer, mode: str = 'list')
-
Base class for other more complex widgets to inherit. An instance of
scrilla.gui.widgets.ArgumentWidget
embeds its child widgets in its own layout, which is an instance ofPySide6.QtWidgetQVBoxLayout
. This class provides access to the following widgets:aPySide6.QtWidgets.QLabel
for the title widget, an error message widget, aPySide6.QtWidgets.QPushButton
for the calculate button widget, aPySide6.QtWidget.QLineEdit
for ticker symbol input, and a variety of optional input widgets enabled by boolean flags in thecontrols
constructor argument.Constructor
- calculate_function:
str
Function attached to thecalculate_button
widget. Triggered when the widget is clicked. - clear_function:
str
Function attached to theclear_button
widget. Trigged when the widget is clicked. - controls::
Dict[bool]
Dictionary of boolean flags instructing the class which optional input to include. Optional keys can be found inARG_DICT
. An dictionary skeleton with all the optional input disabled can be retrieved throughgenerate_control_skeleton()
. - layer:
str
Stylesheet property attached to widget. - single:
bool
Flag to limit symbol input to one ticker symbol. Calls toget_symbol_input()
will return
Attributes
- title:
PySide6.QtWidgets.QLabel
- required_title:
PySide6.QtWidgets.QLabel
- optional_title:
PySide6.QtWidgets.QLabel
- required_pane:
PySide6.QtWidgets.QLabel
- optional_pane:
PySide6.QtWidgets.QLabel
- message:
PySide6.QtWidgets.QLabel
Label attached toself.symbol_input
. - error_message:
PySide6.QtWidget.QLabel
Message displayed if there is an error thrown during calculation. - calculate_button:
PySide6.QtWidget.QPushButton
Empty button for super classes to inherit, primed to fire events on return presses - clear_button:
PySide6.QtWidget.QPushButton
Empty button for super classes to inherit, primed to fire events on return presses. - symbol_widget:
PySide6.QtWidget.QLineEdit
Empty text area for super classes to inherit - control_widgets: ``Dict[str,Union[PySide6.QtWidgets.QWidget,None]] Dictionary containing the optional input widgets.
init(self, parent: Optional[PySide6.QtWidgets.QWidget] = None, f: PySide6.QtCore.Qt.WindowFlags = Default(Qt.WindowFlags)) -> None
Initialize self. See help(type(self)) for accurate signature.
Expand source code
class ArgumentWidget(QtWidgets.QWidget): """ Base class for other more complex widgets to inherit. An instance of `scrilla.gui.widgets.ArgumentWidget` embeds its child widgets in its own layout, which is an instance of``PySide6.QtWidgetQVBoxLayout``. This class provides access to the following widgets:a ``PySide6.QtWidgets.QLabel`` for the title widget, an error message widget, a ``PySide6.QtWidgets.QPushButton`` for the calculate button widget, a ``PySide6.QtWidget.QLineEdit`` for ticker symbol input, and a variety of optional input widgets enabled by boolean flags in the `controls` constructor argument. Constructor ----------- 1. **calculate_function**: ``str`` Function attached to the `calculate_button` widget. Triggered when the widget is clicked. 2. **clear_function**: ``str`` Function attached to the `clear_button` widget. Trigged when the widget is clicked. 3. **controls:**: ``Dict[bool]`` Dictionary of boolean flags instructing the class which optional input to include. Optional keys can be found in `scrilla.static.definitions.ARG_DICT`. An dictionary skeleton with all the optional input disabled can be retrieved through `scrilla.gui.widgets.factories.generate_control_skeleton`. 4. **layer**: ``str`` Stylesheet property attached to widget. 5. **single**: ``bool`` Flag to limit symbol input to one ticker symbol. Calls to `get_symbol_input()` will return Attributes ---------- 1. **title**: ``PySide6.QtWidgets.QLabel`` 2. **required_title**: ``PySide6.QtWidgets.QLabel`` 3. **optional_title**: ``PySide6.QtWidgets.QLabel`` 4. **required_pane**: ``PySide6.QtWidgets.QLabel`` 5. **optional_pane**: ``PySide6.QtWidgets.QLabel`` 6. **message**: ``PySide6.QtWidgets.QLabel`` Label attached to `self.symbol_input`. 7. **error_message**: ``PySide6.QtWidget.QLabel`` Message displayed if there is an error thrown during calculation. 8. **calculate_button**: ``PySide6.QtWidget.QPushButton`` Empty button for super classes to inherit, primed to fire events on return presses 9. **clear_button**: ``PySide6.QtWidget.QPushButton`` Empty button for super classes to inherit, primed to fire events on return presses. 10. **symbol_widget**: ``PySide6.QtWidget.QLineEdit`` Empty text area for super classes to inherit 11. **control_widgets**: ``Dict[str,Union[PySide6.QtWidgets.QWidget,None]] Dictionary containing the optional input widgets. """ # TODO: calculate and clear should be part of THIS constructor, doesn't make sense to have other widgets hook them up. def __init__(self, calculate_function: Callable, clear_function: Callable, controls: Dict[str, bool], layer, mode: str = SYMBOLS_LIST): super().__init__() self.layer = layer self.controls = controls self.control_widgets = {} self.group_control_widgets = None self.calculate_function = calculate_function self.clear_function = clear_function self._init_widgets(mode) self._generate_group_widgets() self._arrange_widgets() self._stage_widgets() def _init_widgets(self, mode: str) -> None: """ Creates child widgets by calling factory methods from `scrilla.gui.widgets.factories`. This method will iterate over `self.controls` and initialize the optional input widget accordingly. `self.optional_pane` and `self.required_pane`, the container widgets for the input elements, are initialized with a style tag on the same layer as the `scrilla.gui.widgets.components.ArgumentWidget`. """ self.title = factories.atomic_widget_factory( component='subtitle', title='Function Input') self.optional_title = factories.atomic_widget_factory( component='label', title='Optional Arguments') self.error_message = factories.atomic_widget_factory( component='error', title="Error Message Goes Here") self.calculate_button = factories.atomic_widget_factory( component='calculate-button', title='Calculate') self.clear_button = factories.atomic_widget_factory( component='clear-button', title='Clear') if mode == SYMBOLS_LIST: self.required_title = factories.atomic_widget_factory( component='label', title='Required Arguments') self.symbol_hint = factories.atomic_widget_factory( component='text', title="Separate Tickers With Commas") self.required_pane = factories.layout_factory( layout='vertical-box') self.required_pane.setObjectName(self.layer) self.symbol_widget = factories.argument_widget_factory( component='symbols', title="Asset Tickers :", optional=False) elif mode == SYMBOLS_SINGLE: self.required_title = factories.atomic_widget_factory( component='label', title='Required Argument') self.symbol_hint = factories.atomic_widget_factory( component='text', title="Enter a Single Symbol") self.required_pane = factories.layout_factory( layout='vertical-box') self.required_pane.setObjectName(self.layer) self.symbol_widget = factories.argument_widget_factory( component='symbol', title="Symbol: ", optional=False) else: self.symbol_widget = None self.group_definitions = None for control in self.controls: if self.controls[control]: if self.controls[control]: if definitions.ARG_DICT[control]["widget_type"] != "group": self.control_widgets[control] = factories.argument_widget_factory(definitions.ARG_DICT[control]['widget_type'], f'{definitions.ARG_DICT[control]["name"]} :', optional=True) else: if self.group_definitions is None: self.group_definitions = {} self.group_definitions[definitions.ARG_DICT[control] ['name']] = definitions.ARG_DICT[control] else: self.control_widgets[control] = None self.optional_pane = factories.layout_factory(layout='vertical-box') self.optional_pane.setObjectName(self.layer) self.setLayout(QtWidgets.QVBoxLayout()) def _generate_group_widgets(self): if self.group_definitions is not None: self.group_control_widgets = {} groups = {} for definition in self.group_definitions: group_key = self.group_definitions[definition]['group'] group_name = definitions.GROUP_DICT[group_key] if group_name not in groups.keys(): groups[group_name] = [definition] else: groups[group_name].append(definition) for group_name, group in groups.items(): self.group_control_widgets[group_name] = factories.group_widget_factory( group, group_name) def _arrange_widgets(self): """ Arrange child widgets in their layouts and provides rendering hints. The `self.symbol_widget` is set into a ``PySide6.QtWidgets.QVBoxLayout`` named `self.required_pane`. The optional input widgets are set into a separate ``PySide6.QtWidgets.QVBoxLayout`` named `self.optional_pane`. `self.required_pane` and `self.optional_pane` are in turn set into a parent `PySide6.QtWidgets.QVBoxLayout``, along with `self.calculate_button` and `self.clear_button`. A strecth widget is inserted between the input widgets and the button widgets. """ factories.set_policy_on_widget_list([self.title, self.optional_title, self.optional_pane], QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum)) if self.symbol_widget is not None: factories.set_policy_on_widget_list([self.symbol_hint, self.required_title, self.required_pane], QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum)) self.setSizePolicy(QtWidgets.QSizePolicy( QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)) if self.symbol_widget is not None: self.required_pane.layout().addWidget(self.required_title) self.required_pane.layout().addWidget(self.symbol_hint) self.required_pane.layout().addWidget(self.symbol_widget) if self.group_control_widgets is not None: for widget in self.group_control_widgets.values(): self.required_pane.layout().addWidget(widget) self.optional_pane.layout().addWidget(self.optional_title) for control_widget in self.control_widgets.values(): if control_widget is not None: self.optional_pane.layout().addWidget(control_widget) self.layout().addWidget(self.title) self.layout().addWidget(self.error_message) if self.symbol_widget is not None: self.layout().addWidget(self.required_pane) self.layout().addWidget(self.optional_pane) self.layout().addStretch() self.layout().addWidget(self.calculate_button) self.layout().addWidget(self.clear_button) def _stage_widgets(self): """ Prepares child widgets for display. `self.clear_function` and `self.calculate` function are hooked into the `clicked` signal emitted from `self.clear_button` and `self.calculate_button`, respectively. """ self.error_message.hide() self.clear_button.clicked.connect(self.clear_function) self.calculate_button.clicked.connect(self.calculate_function) if self.symbol_widget is not None: # NOTE: symbol widget is technically a layout in which the lineedit is abutted by a label, so need # to pull the actual input element from the layout self.symbol_widget.layout().itemAt(1).widget( ).returnPressed.connect(self.calculate_function) def get_symbol_input(self) -> Union[List[str], None]: """ Returns the symbols inputted into the `PySide6.QtWidgets.QLineEdit` child of `symbol_widget`. If the `ArgumentWidget` has been initialized as a `scrilla.gui.widgets.components.SYMBOLS_LIST`, the method will return a list of inputted strings. If the `ArgumentWidget` has been initialied as a `scrilla.gui.widgets.components.SYMBOLS_SINGLE`, the method will return a list with a string as its single element. If the `ArgumentWidget` has been initialized as a `scrilla.gui.widgets.components.SYMBOLS_NONE`, this method will return `None`. In addition, in the first two cases, if no input has been entered, this method will return `None`. """ if self.symbol_widget is not None: return helper.split_and_strip(self.symbol_widget.layout().itemAt(1).widget().text()) return None def get_control_input(self, control_widget_key: str) -> Union[datetime.date, str, bool, None]: """ Get the value on the specified optional input widget. Optional keys are accessed through the keys of the `scrilla.static.definitions.ARG_DICT` dictionary. If the widget is disabled or has been excluded altogether from the parent widget, i.e. a value of `False` was passed in through the constructor's `controls` arguments for that optional input widget, this method will return `None`. """ if self.control_widgets[control_widget_key] is None: return None widget = self.control_widgets[control_widget_key].layout().itemAt( 1).widget() if not widget.isEnabled(): return None if type(widget) is QtWidgets.QDateEdit: return widget.date().toPython() if type(widget) is QtWidgets.QLineEdit: if type(widget.validator()) is QtGui.QIntValidator: return int(widget.text()) if type(widget.validator()) in [QtGui.QDoubleValidator, QtGui.QRegularExpressionValidator]: return float(widget.text()) return widget.text() if type(widget) is QtWidgets.QRadioButton: return widget.isChecked() def prime(self) -> None: """ Enables user input on child widgets, except `clear_button` which is disabled. """ self.clear_button.hide() self.calculate_button.show() if self.symbol_widget is not None: self.symbol_widget.layout().itemAt(1).widget().setEnabled(True) self.symbol_widget.layout().itemAt(1).widget().clear() for control in self.control_widgets: if self.control_widgets[control] is not None: if type(self.control_widgets[control].layout().itemAt(1).widget()) != QtWidgets.QRadioButton: self.control_widgets[control].layout().itemAt( 1).widget().clear() self.control_widgets[control].layout().itemAt( 2).widget().setEnabled(True) self.control_widgets[control].layout().itemAt( 2).widget().setCheckState(QtCore.Qt.Unchecked) self.control_widgets[control].layout().itemAt( 1).widget().setEnabled(False) # NOTE: have to set Widget1 to disabled last since the signal on the Widget2 is connected # through CheckState to the enabled stated of Widget1, i.e. flipping the CheckState on # Widget2 switches the enabled state of Widget1. def fire(self) -> None: """ Disables user input on child widgets, except `clear_button` which is enabled. """ self.clear_button.show() self.calculate_button.hide() if self.symbol_widget is not None: self.symbol_widget.layout().itemAt(1).widget().setEnabled(False) for control in self.control_widgets: if self.control_widgets[control] is not None: self.control_widgets[control].layout().itemAt( 1).widget().setEnabled(False) self.control_widgets[control].layout().itemAt( 2).widget().setEnabled(False)
Ancestors
- PySide6.QtWidgets.QWidget
- PySide6.QtCore.QObject
- PySide6.QtGui.QPaintDevice
- Shiboken.Object
Class variables
var staticMetaObject
Methods
def fire(self) ‑> None
-
Disables user input on child widgets, except
clear_button
which is enabled.Expand source code
def fire(self) -> None: """ Disables user input on child widgets, except `clear_button` which is enabled. """ self.clear_button.show() self.calculate_button.hide() if self.symbol_widget is not None: self.symbol_widget.layout().itemAt(1).widget().setEnabled(False) for control in self.control_widgets: if self.control_widgets[control] is not None: self.control_widgets[control].layout().itemAt( 1).widget().setEnabled(False) self.control_widgets[control].layout().itemAt( 2).widget().setEnabled(False)
def get_control_input(self, control_widget_key: str) ‑> Union[datetime.date, str, bool, ForwardRef(None)]
-
Get the value on the specified optional input widget. Optional keys are accessed through the keys of the
ARG_DICT
dictionary.If the widget is disabled or has been excluded altogether from the parent widget, i.e. a value of
False
was passed in through the constructor'scontrols
arguments for that optional input widget, this method will returnNone
.Expand source code
def get_control_input(self, control_widget_key: str) -> Union[datetime.date, str, bool, None]: """ Get the value on the specified optional input widget. Optional keys are accessed through the keys of the `scrilla.static.definitions.ARG_DICT` dictionary. If the widget is disabled or has been excluded altogether from the parent widget, i.e. a value of `False` was passed in through the constructor's `controls` arguments for that optional input widget, this method will return `None`. """ if self.control_widgets[control_widget_key] is None: return None widget = self.control_widgets[control_widget_key].layout().itemAt( 1).widget() if not widget.isEnabled(): return None if type(widget) is QtWidgets.QDateEdit: return widget.date().toPython() if type(widget) is QtWidgets.QLineEdit: if type(widget.validator()) is QtGui.QIntValidator: return int(widget.text()) if type(widget.validator()) in [QtGui.QDoubleValidator, QtGui.QRegularExpressionValidator]: return float(widget.text()) return widget.text() if type(widget) is QtWidgets.QRadioButton: return widget.isChecked()
def get_symbol_input(self) ‑> Optional[List[str]]
-
Returns the symbols inputted into the
PySide6.QtWidgets.QLineEdit
child ofsymbol_widget
. If theArgumentWidget
has been initialized as aSYMBOLS_LIST
, the method will return a list of inputted strings. If theArgumentWidget
has been initialied as aSYMBOLS_SINGLE
, the method will return a list with a string as its single element. If theArgumentWidget
has been initialized as aSYMBOLS_NONE
, this method will returnNone
. In addition, in the first two cases, if no input has been entered, this method will returnNone
.Expand source code
def get_symbol_input(self) -> Union[List[str], None]: """ Returns the symbols inputted into the `PySide6.QtWidgets.QLineEdit` child of `symbol_widget`. If the `ArgumentWidget` has been initialized as a `scrilla.gui.widgets.components.SYMBOLS_LIST`, the method will return a list of inputted strings. If the `ArgumentWidget` has been initialied as a `scrilla.gui.widgets.components.SYMBOLS_SINGLE`, the method will return a list with a string as its single element. If the `ArgumentWidget` has been initialized as a `scrilla.gui.widgets.components.SYMBOLS_NONE`, this method will return `None`. In addition, in the first two cases, if no input has been entered, this method will return `None`. """ if self.symbol_widget is not None: return helper.split_and_strip(self.symbol_widget.layout().itemAt(1).widget().text()) return None
def prime(self) ‑> None
-
Enables user input on child widgets, except
clear_button
which is disabled.Expand source code
def prime(self) -> None: """ Enables user input on child widgets, except `clear_button` which is disabled. """ self.clear_button.hide() self.calculate_button.show() if self.symbol_widget is not None: self.symbol_widget.layout().itemAt(1).widget().setEnabled(True) self.symbol_widget.layout().itemAt(1).widget().clear() for control in self.control_widgets: if self.control_widgets[control] is not None: if type(self.control_widgets[control].layout().itemAt(1).widget()) != QtWidgets.QRadioButton: self.control_widgets[control].layout().itemAt( 1).widget().clear() self.control_widgets[control].layout().itemAt( 2).widget().setEnabled(True) self.control_widgets[control].layout().itemAt( 2).widget().setCheckState(QtCore.Qt.Unchecked) self.control_widgets[control].layout().itemAt( 1).widget().setEnabled(False) # NOTE: have to set Widget1 to disabled last since the signal on the Widget2 is connected # through CheckState to the enabled stated of Widget1, i.e. flipping the CheckState on # Widget2 switches the enabled state of Widget1.
- calculate_function:
class CompositeWidget (tmp_graph_key: str, layer: str, widget_title: str = 'Results', table_title: Optional[str] = None, graph_title: Optional[str] = None)
-
Constructor
- tmp_graph_key:
str
- widget_title:
str
Optional. Defaults to "Results". Title of the widget. - table_title:
Union[str,None]
- graph_title:
Union[str,None]
Attributes
- title:
PySide6.QtWidgets.QLabel
- table_widget:
scrilla.gui.widgets.TableWidget
- graph_widget:
scrilla.gui.widgets.GraphWidget
- tab_widget:
PySide6.QtWidget.QTabWidget
init(self, parent: Optional[PySide6.QtWidgets.QWidget] = None, f: PySide6.QtCore.Qt.WindowFlags = Default(Qt.WindowFlags)) -> None
Initialize self. See help(type(self)) for accurate signature.
Expand source code
class CompositeWidget(QtWidgets.QWidget): """ Constructor ----------- 1. **tmp_graph_key**: ``str`` 2. **widget_title**: ``str`` *Optional*. Defaults to "Results". Title of the widget. 3. **table_title**: ``Union[str,None]`` 4. **graph_title**: ``Union[str,None]`` Attributes ---------- 1. **title**: ``PySide6.QtWidgets.QLabel`` 2. **table_widget**: ``scrilla.gui.widgets.TableWidget`` 3. **graph_widget**: ``scrilla.gui.widgets.GraphWidget`` 4. **tab_widget**: ``PySide6.QtWidget.QTabWidget`` """ def __init__(self, tmp_graph_key: str, layer: str, widget_title: str = "Results", table_title: Union[str, None] = None, graph_title: Union[str, None] = None): super().__init__() self.setObjectName(layer) self._init_widgets(widget_title=widget_title, tmp_graph_key=tmp_graph_key) self._arrange_widgets(graph_title=graph_title, table_title=table_title) def _init_widgets(self, widget_title: str, tmp_graph_key: str) -> None: """Creates child widgets and their layouts""" self.title = factories.atomic_widget_factory( component='subtitle', title=widget_title) self.table_widget = TableWidget(layer=self.objectName()) self.graph_widget = GraphWidget( tmp_graph_key=tmp_graph_key, layer=self.objectName()) self.tab_widget = QtWidgets.QTabWidget() self.setLayout(QtWidgets.QVBoxLayout()) def _arrange_widgets(self, graph_title: str, table_title: str) -> None: self.title.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) self.tab_widget.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)) self.title.setAlignment(QtCore.Qt.AlignLeft) self.tab_widget.addTab(self.table_widget, table_title) self.tab_widget.addTab(self.graph_widget, graph_title) self.layout().addWidget(self.title) self.layout().addWidget(self.tab_widget)
Ancestors
- PySide6.QtWidgets.QWidget
- PySide6.QtCore.QObject
- PySide6.QtGui.QPaintDevice
- Shiboken.Object
Class variables
var staticMetaObject
- tmp_graph_key:
class GraphWidget (tmp_graph_key: str, layer: str, widget_title: str = 'Graph Results')
-
Base class for other more complex widgets to inherit. An instance of
scrilla.gui.widgets.GraphWidget
embeds its child widgets in its own layout, which is an instance ofPySide6.QtWidgetQVBoxLayout
. This class provides access to the following widgets: aPySide6.QtWidgets.QLabel
for the title widget, an error message widget and aPySide6.QtWidgets.QTableWidget
for results display.The graph to be displayed should be stored in the
TEMP_DIR
directory before calling theshow_pixmap
method on this class. It will load the graph from file and convert it into aPySide6.QtGui.QPixMap
.Parameters
- tmp_graph_key:
str
The key of the file in theTEMP_DIR
used to store the image of the graph. - widget_title:
str
Optional. Defaults to "Graph Results". Title of the widget.
Attributes
- tmp_graph_key:
str
- title:
PySide6.QtWidget.QLabel
- figure:
PySide6.QtWidget.QLabel
init(self, parent: Optional[PySide6.QtWidgets.QWidget] = None, f: PySide6.QtCore.Qt.WindowFlags = Default(Qt.WindowFlags)) -> None
Initialize self. See help(type(self)) for accurate signature.
Expand source code
class GraphWidget(QtWidgets.QWidget): """ Base class for other more complex widgets to inherit. An instance of `scrilla.gui.widgets.GraphWidget` embeds its child widgets in its own layout, which is an instance of``PySide6.QtWidgetQVBoxLayout``. This class provides access to the following widgets: a ``PySide6.QtWidgets.QLabel`` for the title widget, an error message widget and a ``PySide6.QtWidgets.QTableWidget`` for results display. The graph to be displayed should be stored in the `scrilla.settings.TEMP_DIR` directory before calling the `show_pixmap` method on this class. It will load the graph from file and convert it into a ``PySide6.QtGui.QPixMap``. Parameters ---------- 1. **tmp_graph_key**: ``str`` The key of the file in the `scrilla.settings.TEMP_DIR` used to store the image of the graph. 2. **widget_title**: ``str`` *Optional*. Defaults to "Graph Results". Title of the widget. Attributes ---------- 1. **tmp_graph_key**: ``str`` 2. **title**: ``PySide6.QtWidget.QLabel`` 3. **figure**: ``PySide6.QtWidget.QLabel`` """ def __init__(self, tmp_graph_key: str, layer: str, widget_title: str = "Graph Results"): super().__init__() self.layer = layer self.tmp_graph_key = tmp_graph_key self._init_widgets(widget_title) self._arrange_widgets() self._stage_widgets() def _init_widgets(self, widget_title: str) -> None: self.title_container = factories.layout_factory( layout='horizontal-box') self.title = factories.atomic_widget_factory( component='heading', title=widget_title) self.download_button = factories.atomic_widget_factory( component='download-button', title=None) self.figure = factories.atomic_widget_factory( component='figure', title=None) self.setLayout(QtWidgets.QVBoxLayout()) def _arrange_widgets(self) -> None: factories.set_policy_on_widget_list([self, self.title, self.title_container], QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) self.title_container.layout().addWidget(self.title) self.title_container.layout().addWidget(self.download_button) self.layout().addWidget(self.title_container) self.layout().addWidget(self.figure) def _stage_widgets(self) -> None: self.figure.hide() self.download_button.hide() self.download_button.clicked.connect(self.show_file_dialog) def set_pixmap(self) -> None: self.figure.setPixmap(utilities.generate_pixmap_from_temp( self.width(), self.height(), self.tmp_graph_key)) self.figure.show() self.download_button.show() def clear(self): self.figure.hide() self.download_button.hide() @QtCore.Slot() def show_file_dialog(self) -> None: file_path = factories.atomic_widget_factory( component='save-dialog', title=f'(*.{settings.IMG_EXT})') file_path.selectFile(f'{self.tmp_graph_key}.{settings.IMG_EXT}') filename = None if file_path.exec_() == QtWidgets.QDialog.Accepted: filename = file_path.selectedFiles() if filename is not None and len(filename) > 0: utilities.download_tmp_to_file(self.tmp_graph_key, filename[0])
Ancestors
- PySide6.QtWidgets.QWidget
- PySide6.QtCore.QObject
- PySide6.QtGui.QPaintDevice
- Shiboken.Object
Class variables
var staticMetaObject
Methods
def clear(self)
-
Expand source code
def clear(self): self.figure.hide() self.download_button.hide()
def set_pixmap(self) ‑> None
-
Expand source code
def set_pixmap(self) -> None: self.figure.setPixmap(utilities.generate_pixmap_from_temp( self.width(), self.height(), self.tmp_graph_key)) self.figure.show() self.download_button.show()
def show_file_dialog(self) ‑> None
-
Expand source code
@QtCore.Slot() def show_file_dialog(self) -> None: file_path = factories.atomic_widget_factory( component='save-dialog', title=f'(*.{settings.IMG_EXT})') file_path.selectFile(f'{self.tmp_graph_key}.{settings.IMG_EXT}') filename = None if file_path.exec_() == QtWidgets.QDialog.Accepted: filename = file_path.selectedFiles() if filename is not None and len(filename) > 0: utilities.download_tmp_to_file(self.tmp_graph_key, filename[0])
- tmp_graph_key:
class SkeletonWidget (function: str, parent: PySide6.QtWidgets.QWidget)
-
QWidget(self, parent: Optional[PySide6.QtWidgets.QWidget] = None, f: PySide6.QtCore.Qt.WindowFlags = Default(Qt.WindowFlags)) -> None
init(self, parent: Optional[PySide6.QtWidgets.QWidget] = None, f: PySide6.QtCore.Qt.WindowFlags = Default(Qt.WindowFlags)) -> None
Initialize self. See help(type(self)) for accurate signature.
Expand source code
class SkeletonWidget(QtWidgets.QWidget): def __init__(self, function: str, parent: QtWidgets.QWidget): super(SkeletonWidget, self).__init__(parent) self._configure_control_skeleton(function) def _configure_control_skeleton(self, function: str): self.controls = factories.generate_control_skeleton() for arg in definitions.FUNC_DICT[function]['args']: if not definitions.ARG_DICT[arg]['cli_only']: self.controls[arg] = True
Ancestors
- PySide6.QtWidgets.QWidget
- PySide6.QtCore.QObject
- PySide6.QtGui.QPaintDevice
- Shiboken.Object
Subclasses
- CorrelationWidget
- DiscountDividendWidget
- DistributionWidget
- EfficientFrontierWidget
- MovingAverageWidget
- OptimizerWidget
- RiskProfileWidget
- YieldCurveWidget
Class variables
var staticMetaObject
class TableWidget (layer, widget_title: str = 'Table Result')
-
Base class for other more complex widgets to inherit. An instance of
scrilla.gui.widgets.TableWidget
embeds its child widgets in its own layout, which is an instance ofPySide6.QtWidgetQVBoxLayout
. This class provides access to the following widgets: aPySide6.QtWidgets.QLabel
for the title widget, an error message widget and aPySide6.QtWidgets.QTableWidget
for results display.Parameters
- layer:
str
Stylesheet property attached to widget. - widget_title:
str
Optional. Defaults to "Table Result". Title of the widget.
Attributes
- title:
PySide6.QtWidget.QLabel
2.. table:PySide6.QtWidget.QTableWidget
init(self, parent: Optional[PySide6.QtWidgets.QWidget] = None, f: PySide6.QtCore.Qt.WindowFlags = Default(Qt.WindowFlags)) -> None
Initialize self. See help(type(self)) for accurate signature.
Expand source code
class TableWidget(QtWidgets.QWidget): """ Base class for other more complex widgets to inherit. An instance of `scrilla.gui.widgets.TableWidget` embeds its child widgets in its own layout, which is an instance of``PySide6.QtWidgetQVBoxLayout``. This class provides access to the following widgets: a ``PySide6.QtWidgets.QLabel`` for the title widget, an error message widget and a ``PySide6.QtWidgets.QTableWidget`` for results display. Parameters ---------- 1. **layer**: ``str`` Stylesheet property attached to widget. 2. **widget_title**: ``str`` *Optional*. Defaults to "Table Result". Title of the widget. Attributes ---------- 1. **title**: ``PySide6.QtWidget.QLabel`` 2.. **table**: ``PySide6.QtWidget.QTableWidget`` """ def __init__(self, layer, widget_title: str = "Table Result"): super().__init__() self.layer = layer self._init_widgets(widget_title) self._arrange_widgets() self._stage_widgets() def _init_widgets(self, widget_title: str) -> None: """Creates child widgets and their layouts""" self.title_container = factories.layout_factory( layout='horizontal-box') self.title = factories.atomic_widget_factory( component='heading', title=widget_title) self.download_button = factories.atomic_widget_factory( component='download-button', title=None) self.table = factories.atomic_widget_factory( component='table', title=None) self.setLayout(QtWidgets.QVBoxLayout()) def _arrange_widgets(self) -> None: factories.set_policy_on_widget_list([self, self.title, self.title_container], QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) self.title_container.layout().addWidget(self.title) self.title_container.layout().addWidget(self.download_button) self.layout().addWidget(self.title_container) self.layout().addWidget(self.table, 1) def _stage_widgets(self) -> None: self.table.hide() self.download_button.hide() self.download_button.clicked.connect(self.show_file_dialog) def init_table(self, rows: List[str], columns: List[str]) -> None: """ Initializes `table` for display. Number of rows and columns is determined by the length of the passed in lists. Parameters ---------- 1. **rows**: ``List[str]`` List containing the row headers. 2. **columns**: ``List[str]`` List containing the column headers. """ self.table.setRowCount(len(rows)) self.table.setColumnCount(len(columns)) self.table.setHorizontalHeaderLabels(columns) self.table.setVerticalHeaderLabels(rows) def show_table(self): self.table.resizeColumnsToContents() self.table.show() self.download_button.show() @QtCore.Slot() def show_file_dialog(self) -> None: file_path = factories.atomic_widget_factory( component='save-dialog', title=f'(*.{settings.FILE_EXT})') file_path.selectFile(f'table.{settings.FILE_EXT}') filename = None if file_path.exec_() == QtWidgets.QDialog.Accepted: filename = file_path.selectedFiles() if filename is not None and len(filename) > 0: if settings.FILE_EXT == 'json': utilities.download_table_to_json(self.table, filename[0])
Ancestors
- PySide6.QtWidgets.QWidget
- PySide6.QtCore.QObject
- PySide6.QtGui.QPaintDevice
- Shiboken.Object
Class variables
var staticMetaObject
Methods
def init_table(self, rows: List[str], columns: List[str]) ‑> None
-
Initializes
table
for display. Number of rows and columns is determined by the length of the passed in lists.Parameters
- rows:
List[str]
List containing the row headers. - columns:
List[str]
List containing the column headers.
Expand source code
def init_table(self, rows: List[str], columns: List[str]) -> None: """ Initializes `table` for display. Number of rows and columns is determined by the length of the passed in lists. Parameters ---------- 1. **rows**: ``List[str]`` List containing the row headers. 2. **columns**: ``List[str]`` List containing the column headers. """ self.table.setRowCount(len(rows)) self.table.setColumnCount(len(columns)) self.table.setHorizontalHeaderLabels(columns) self.table.setVerticalHeaderLabels(rows)
- rows:
def show_file_dialog(self) ‑> None
-
Expand source code
@QtCore.Slot() def show_file_dialog(self) -> None: file_path = factories.atomic_widget_factory( component='save-dialog', title=f'(*.{settings.FILE_EXT})') file_path.selectFile(f'table.{settings.FILE_EXT}') filename = None if file_path.exec_() == QtWidgets.QDialog.Accepted: filename = file_path.selectedFiles() if filename is not None and len(filename) > 0: if settings.FILE_EXT == 'json': utilities.download_table_to_json(self.table, filename[0])
def show_table(self)
-
Expand source code
def show_table(self): self.table.resizeColumnsToContents() self.table.show() self.download_button.show()
- layer: