"""
Collection of widgets common to multiple BioLuminate panels.
Copyright (c) Schrodinger, LLC. All rights reserved
"""
#- Imports -------------------------------------------------------------------
import os
import schrodinger.ui.qt.filedialog as filedialog
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtGui
from schrodinger.Qt import QtWidgets
from schrodinger.ui.qt import layout
from schrodinger.ui.qt.standard.colors import LightModeColors
from schrodinger.ui.qt.swidgets import SpinnerIcon # noqa: F401
from .actions import icons
from .actions.configs import IMPORT_STRUCTURES
from .actions.factory import Factory
#- Classes -------------------------------------------------------------------
[docs]class SequenceFileDialog(filedialog.FileDialog):
"""
Custom class to handle opening files related to
sequences.
"""
CAPTION = 'Import Sequences'
DEFAULT_FILTERS = ';;'.join([
'FASTA (*.fasta *.fst *.fas *.seq)',
'Maestro (*.mae *.maegz *.mae.gz )', 'PDB (*.pdb *.ent)',
'SWISSPROT (*.sw *.sp *.swiss *.swissprot)', 'GCG (*.gcg *.msf)',
'EMBL (*.embl *.emb)', 'PIR (*.pir)', 'All Files (*.*)'
])
"""
The default MSV filters.
"""
REFERENCE_FILTERS = ';;'.join([
'Common (*.fasta *.fst *.fas *.seq *.mae *.maegz *.mae.gz *.pdb *.ent '
'*.txt)', 'FASTA (*.fasta *.fst *.fas *.seq)', 'PDB (*.pdb *.ent)',
'Maestro (*.mae *.maegz *.mae.gz )', 'All Files (*.*)'
])
"""
Filters for reference sequences in homology model building.
"""
STRUCTURE_FILTERS = ';;'.join([
'Structure files (*.mae *.maegz *.mae.gz *.pdb *.ent)',
'Maestro (*.mae *.maegz *.mae.gz )',
'PDB (*.pdb *.ent)',
])
"""
Filters that have structures associated with them.
"""
[docs] def __init__(self, parent=None, add_options=True, **kwargs):
caption = kwargs.get('caption', self.CAPTION)
filters = kwargs.get('filter', self.DEFAULT_FILTERS)
if not parent:
super(SequenceFileDialog, self).__init__()
self.setWindowTitle(caption)
self.setNameFilter(filters)
else:
kwargs['caption'] = caption
kwargs['filter'] = filters
super(SequenceFileDialog, self).__init__(parent, **kwargs)
self.setAcceptMode(self.AcceptOpen)
self.setLabelText(self.Accept, 'Open')
if add_options:
self.addOptions()
[docs] def addOptions(self):
"""
Adds three widgets on the bottom of the dialog window that allow
users to optionally:
- Align to query sequence
- Replace matching sequences
- Incorporate PDB files into Maestro
"""
self.merge_cb = QtWidgets.QCheckBox("Align to query sequence")
self.replace_cb = QtWidgets.QCheckBox("Replace matching sequences")
self.incorporate_cb = QtWidgets.QCheckBox(
"Incorporate PDB files into Maestro")
grid = self.layout()
row = grid.rowCount()
grid.addWidget(self.merge_cb, row, 1)
grid.addWidget(self.replace_cb, row + 1, 1)
grid.addWidget(self.incorporate_cb, row + 2, 1)
[docs] def getOpenFileName(self, multiple=False):
if multiple:
self.setFileMode(self.ExistingFiles)
elif multiple == False:
self.setFileMode(self.ExistingFile)
else:
raise RuntimeError('The "multiple" arg must be True or False.')
# If not cancelled
if self.exec():
files = [os.path.normpath(str(x)) for x in self.selectedFiles()]
if not multiple:
files = files[0]
return files
return []
[docs] @staticmethod
def get_open_file_names(parent=None,
add_options=True,
multiple=True,
**kwargs):
dialog = SequenceFileDialog(parent, add_options=add_options, **kwargs)
return dialog.getOpenFileName(multiple)
[docs] @staticmethod
def get_open_file_name(parent=None, add_options=True, **kwargs):
dialog = SequenceFileDialog.get_open_file_names(parent=parent,
add_options=add_options,
multiple=False,
**kwargs)
return dialog
[docs]class NumericLineEdit(QtWidgets.QLineEdit):
"""
A `QtWidgets.QLineEdit` with a builtin validator for floats or integers.
"""
[docs] def __init__(self,
parent=None,
width=50,
validate_type='float',
minimum=None,
maximum=None):
QtWidgets.QLineEdit.__init__(self, parent=parent)
self.setMaximumSize(QtCore.QSize(width, 16777215))
if not minimum:
minimum = 0.0
if not maximum:
maximum = 100000000.0
if validate_type == 'float':
self.setValidator(QtGui.QDoubleValidator(minimum, maximum, 5, self))
elif validate_type == 'int':
self.setValidator(QtGui.QIntValidator(minimum, maximum, self))
self.textChanged.connect(self.validate)
[docs] def validate(self):
"""
Checks to see if the lineedit has acceptable input and changes the
widget to indicate invalid input. Even with a validator set users can
input invalid args so this helps out.
"""
if not self.hasAcceptableInput() and self.text() != '':
self.setStyleSheet(
f'QLineEdit {{ border: 2px solid {LightModeColors.INVALID_STATE_BORDER}; }}'
)
else:
self.setStyleSheet('QLineEdit { }')
[docs]class RowActionItem(QtWidgets.QWidget):
"""
Custom widget to be used in a table cell. It will create a widget that
contains multiple small push buttons with icons. This will end up looking
like just a small icon in the cell. Multipe actions can be added to a
single cell
"""
[docs] def __init__(self, row):
"""
:param row: The row associated with the cell. This can be anything
(i.e. integer index, row object, etc.) and is used to
track the row.
:type row: mixed
"""
super(RowActionItem, self).__init__()
self.row = row
self.setObjectName('row_action_item')
self._action_items = []
""" A private variable that stores the list of actions for the cell """
self.setStyleSheet(f"""
QPushButton#cell_button {{
background-color: {LightModeColors.STANDARD_BACKGROUND};
border-width: 0px;
border-radius: 3px;
border-color: {LightModeColors.STANDARD_BORDER};
padding-top: 3px;
padding-bottom: 3px;
}}
""")
[docs] def addActionItem(self, icon, tooltip, callback):
"""
Add an action to the cell widget.
:param icon: the icon for the action
:type icon: QtGui.QIcon
:param callback: The callback for the action
:type callback: callable
"""
button = QtWidgets.QPushButton(icon, '', self)
button.setToolTip(tooltip)
button.setObjectName('cell_button')
button.clicked.connect(callback)
self._action_items.append(button)
[docs] def paintEvent(self, *args, **kwargs):
"""
Override the paintEvent to create the layout for the cell.
"""
if not self.layout():
paint_layout = layout.hbox(self._action_items,
spacing=1,
margins=(1, 1, 1, 1))
self.setLayout(paint_layout)
self.layout_set = True
super(RowActionItem, self).paintEvent(*args, **kwargs)