"""
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.swidgets import SpinnerIcon
from schrodinger.ui.qt.standard.colors import LightModeColors
from schrodinger.ui.sequencealignment.sequence_viewer import SequenceViewer
from .actions import icons
from .actions.configs import FIND_PATTERN_ACTIONS
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)