Source code for schrodinger.application.bioluminate.bwidgets

"""
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 ImportWidget(QtWidgets.QWidget): """ """
[docs] def __init__(self, parent): """ Construct widget. """ self.parent = parent super(ImportWidget, self).__init__(parent) self._widgets = []
[docs] def asGrid(self): return layout.grid(self._widgets)
[docs] def createImportWidget(self, button_title, action_params=None, objname=None, action_order=None, icon=icons.MSV_OPEN_FILE): """ Returns a list of widgets that comprise an `ImportWidget`. """ action_params = action_params or IMPORT_STRUCTURES # Set up the actions to parent this widget's parent. All slots # will be in the context of self.parent factory = Factory(self.parent) factory.setActions(action_params, action_order=action_order) self.button = QtWidgets.QPushButton(self.tr(button_title), self.parent) icon = QtGui.QIcon(icon) self.button.setIcon(QtGui.QIcon(icon)) self.button.setMenu(factory.getMenu()) line_edit = QtWidgets.QLineEdit(self.parent) line_edit.setReadOnly(True) line_edit.setMinimumSize(250, 0) widgets = [self.button, line_edit] self._widgets.append(widgets) return widgets
[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)
[docs]class QuickHelpButton(QtWidgets.QPushButton): """ A help button that has a blue gradient """
[docs] def __init__(self, message_title, message_text): super(QuickHelpButton, self).__init__('?') # Set the style up self._setStyle() # Remove the focus which gets rid of the dotted background # when clicked self.setFocusPolicy(QtCore.Qt.NoFocus) # Create a dialog for when the button is clicked self.message_box = QtWidgets.QMessageBox(self) self.message_box.setWindowTitle(self.tr(message_title)) self.message_box.setText(self.tr(message_text)) # Connect the button click to open the dialog self.clicked.connect(self.message_box.exec)
def _setStyle(self): """ Makes the button rounded and gives it a light blue to darker blue gradient """ # Set the object name so this style only effects this button self.setObjectName('help_button') self.setStyleSheet(""" QPushButton#help_button { max-width: 16px; max-height: 16px; min-width: 16px; min-height: 16px; font: bold; border: 2px solid #8f8f91; border-radius: 10px; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #a4ccec, stop: 1 #3282c2); } """)