Source code for schrodinger.application.bioluminate.ssv.viewer

"""
The simplified version of the sequence viewer used in the MSV.

Also provides access to the viewer as a `QtWidgets.QMainWindow` and
as a `QtWidgets.QDockWidget`.

"""
# Contributors: Joshua Williams, Matvey Adzhigirey

#- Imports -------------------------------------------------------------------
import schrodinger.ui.sequencealignment.globals as sv_globals
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtWidgets
from schrodinger.ui.sequencealignment import maestro as sv_maestro
from schrodinger.ui.sequencealignment.sequence_viewer import SequenceViewer

from .. import bwidgets
from ..actions import configs as action_config
from ..actions.factory import Factory
from .toolbars import AntibodyNumberingToolBar
from .toolbars import ConsensusToolBar
from .toolbars import FindToolBar
from .toolbars import ImportToolBar

try:
    from schrodinger.maestro import maestro
except ImportError:
    maestro = None  # For testing outside of Maestro

#- Functions -----------------------------------------------------------------


[docs]def catch_last_added(fn): """ A decorator for action callbacks that deal with importing sequences into the sequence viewer. The decorator will add a `last_sequences_added` property to the class of the decorated method. This will store all sequences added to the viewer in the last import step. This must decorate a class with a `sequence_group` property. """ def dec(self, *args, **kwargs): # Clear the last imported sequences self.last_sequences_imported = [] # Define the current sequences to compare later current_seqs = [] for seq in self.sequence_group.sequences: current_seqs.append(seq) # Run the decorated method result = fn(self, *args, **kwargs) # Find the new sequences and set to last added sequences for seq in self.sequence_group.sequences: if seq not in current_seqs and not seq.isRuler(): self.last_sequences_imported.append(seq) return result return dec
#- Classes -------------------------------------------------------------------
[docs]class SequenceDockWidget(QtWidgets.QDockWidget): """ The dock widget that can set a `ViewerWindow` as the main widget. This makes the ViewerWindow dockable. """ WINDOW_TITLE = 'Sequence Viewer' # Shortcuts for docking features FLOATABLE = QtWidgets.QDockWidget.DockWidgetFloatable MOVABLE = QtWidgets.QDockWidget.DockWidgetMovable CLOSABLE = QtWidgets.QDockWidget.DockWidgetClosable
[docs] def __init__(self): super(SequenceDockWidget, self).__init__() self.setMinimumSize(400, 300) self.topLevelChanged.connect(self.floatChange) # By default set the widget as floatable and movable only. self.setFloatableMovable()
[docs] def floatChange(self, floating=False): """ Slot for callback that is fired when the dock's floating status changes. Currently this only set the dock's window title is it is floating. """ # Try to get the parent panel's title try: parent = self.parentWidget() title = '%s - %s' % (parent.windowTitle(), self.WINDOW_TITLE) except: title = self.WINDOW_TITLE # Set the window's title if floating: self.setWindowTitle(title) else: self.setWindowTitle('')
# If we are on OSX we need to add a Close button when the # widget is floating. Ev:128271 # NOTE: This is being commented out because it deviates from the # standard OSX behavior. #if sys.platform == 'darwin': # if floating: # self.setFloatableMovableClosable() # else: # self.setFloatableMovable()
[docs] def setFloatable(self): """ Configure the dock widget to only be able to pop out, not be draggable. """ self.setFeatures(self.FLOATABLE)
[docs] def setFloatableMovable(self): """ Configure the dock widget to be draggable and be able to pop out. """ self.setFeatures(self.FLOATABLE | self.MOVABLE)
[docs] def setFloatableMovableClosable(self): """ Configure the dock widget to be draggable, be able to pop out and be closable. """ self.setFeatures(self.FLOATABLE | self.MOVABLE | self.CLOSABLE)
[docs] def closeEvent(self, event): """ Override the close event so that when the widget is floating clicking the close button will just dock it. This is a workaround since MacOSX does not have the float button when the panel is floating. """ event.ignore() self.setFloating(False)
[docs]class ViewerWindow(QtWidgets.QMainWindow): """ Provides the `SimplifiedSequenceViewer` with a window to occupy. This has the benefit of adding toolbars and allowing the sequence viewer (which is a `QtWidgets.QSplitter`) to be a stand-alone window. """ WINDOW_TITLE = 'Sequence Viewer' IMPORT_TOOLBAR = 'import_toolbar' UNDO_REDO_TOOLBAR = 'undo_redo_toolbar' FIND_TOOLBAR = 'find_toolbar' ALIGN_TOOLBAR = 'align_toolbar' CONSENSUS_TOOLBAR = 'consensus_toolbar' ANTIBODY_NUM_TOOLBAR = 'antibody_numbering_toolbar' BREAK_TOOLBAR = 'break_toolbar' DEFAULT_TOOLBARS = [ IMPORT_TOOLBAR, UNDO_REDO_TOOLBAR, FIND_TOOLBAR, ALIGN_TOOLBAR, ]
[docs] def __init__(self, parent): """ :param parent: The sequence viewer object that will use this window :type parent: `SimplifiedSequenceViewer` :raise RuntimeError: If `parent` is not correct type """ if not isinstance(parent, SequenceViewer): msg = 'The parent must be an instance of the class:\n' msg += 'schrodinger.ui.sequencealignment.sequence_viewer.SequenceViewer' raise RuntimeError(msg) self.viewer = parent viewer_parent = parent.parent() super(ViewerWindow, self).__init__(viewer_parent) # Try to set the window title in the context of the parent's parent # title. For example if the parent's title is "Antibody Prediction" # this would be "Antibody Prediction - Sequence Viewer" try: title = '%s - %s' % (viewer_parent.windowTitle(), self.WINDOW_TITLE) except: title = self.WINDOW_TITLE self.setWindowTitle(title) self.setCentralWidget(self.viewer) self.action_factory = Factory(self.viewer) """ The factory to use when creating actions. We want to set all the action's parent to the passed in parent, which is a SequenceViewer. All of the actions associated with the viewer are the module, schrodinger.ui.sequencealignment.sequence_viewer. """ self.resize(700, 400)
[docs] def removeToolBar(self, objname=None): """ Overrides the base class's `QtWidgets.QMainWindow.removeToolBar` method to allow for removal of a single toolbar based on the toolbar's object name or, if `objname` is `None`, removal of all the window's toolbars. """ if objname is not None: toolbar = self.findChild(QtWidgets.QToolBar, objname) super(ViewerWindow, self).removeToolBar(toolbar) else: for toolbar in self.findChildren(QtWidgets.QToolBar): super(ViewerWindow, self).removeToolBar(toolbar)
[docs] def addToolBars(self, toolbars): """ Add toolbars to the sequence viewer. The `toolbars` arg should be a list of items taken from the `ViewerWindow` properties: - `ViewerWindow.IMPORT_TOOLBAR` - `ViewerWindow.UNDO_REDO_TOOLBAR` - `ViewerWindow.FIND_TOOLBAR` - `ViewerWindow.ALIGN_TOOLBAR` - `ViewerWindow.CONSENSUS_TOOLBAR` - `ViewerWindow.ANTIBODY_NUM_TOOLBAR` - `ViewerWindow.BREAK_TOOLBAR` """ fmap = { self.IMPORT_TOOLBAR: self.addImportToolBar, self.UNDO_REDO_TOOLBAR: self.addUndoRedoToolBar, self.FIND_TOOLBAR: self.addFindToolBar, self.ALIGN_TOOLBAR: self.addAlignToolBar, self.CONSENSUS_TOOLBAR: self.addConsensusToolBar, self.ANTIBODY_NUM_TOOLBAR: self.addAntibodyNumberingToolBar, self.BREAK_TOOLBAR: self.addToolBarBreak, } for toolbar in toolbars: func = fmap.get(toolbar) if not func: raise AttributeError('Invalid toolbar') func()
[docs] def setToolBars(self, toolbars): """ Deletes all window toolbars and sets them to the new `toolbars` """ self.removeToolBar() self.addToolBars(toolbars)
[docs] def addImportToolBar(self, area=QtCore.Qt.TopToolBarArea): """ Adds an `ImportToolBar` widget to the area indicated (default: top). :param area: The area to add the dock widget to """ toolbar = ImportToolBar(self.viewer, title='Import Toolbar') toolbar.setObjectName(self.IMPORT_TOOLBAR) self.addToolBar(area, toolbar)
[docs] def addUndoRedoToolBar(self, area=QtCore.Qt.TopToolBarArea): """ Adds a widget that handles undo/redo operations to the area indicated (default: top). :param area: The area to add the dock widget to """ self.action_factory.setActions(action_config.SEQUENCE_VIEWER, action_order=['undo', 'redo']) # Method in SequenceViewer class self.viewer.setUndoRedoActions(*self.action_factory.actions) # We set them to false, as the base class will handle the # actions from here for act in self.action_factory.actions: act.setEnabled(False) toolbar = self.action_factory.getToolBar(title='Undo/Redo Toolbar') toolbar.setObjectName(self.UNDO_REDO_TOOLBAR) self.addToolBar(area, toolbar)
[docs] def addFindToolBar(self, area=QtCore.Qt.TopToolBarArea): """ Adds a `FindToolBar` widget to the area indicated (default: top). :param area: The area to add the dock widget to """ toolbar = FindToolBar(self.viewer, title='Find Pattern Toolbar') toolbar.setObjectName(self.FIND_TOOLBAR) self.addToolBar(area, toolbar)
[docs] def addAlignToolBar(self, area=QtCore.Qt.BottomToolBarArea): """ Adds a toolbar to the area indicated (default: bottom) that contains all of the actions for alignment. :param area: The area to add the dock widget to """ self.action_factory.setActions(action_config.ALIGNMENT_ACTIONS) toolbar = self.action_factory.getToolBar(title='Alignment Toolbar') toolbar.setObjectName(self.ALIGN_TOOLBAR) self.addToolBar(area, toolbar)
[docs] def addConsensusToolBar(self, area=QtCore.Qt.BottomToolBarArea): """ Adds a `ConsensusToolBar` widget to the area indicated (default: bottom) that contains all of the actions for consensus visualization. :param area: The area to add the dock widget to """ toolbar = ConsensusToolBar(self.viewer) toolbar.setObjectName(self.CONSENSUS_TOOLBAR) self.addToolBar(area, toolbar) # When files are imported we want to update the toolbar's # action states self.viewer.sequencesImported.connect(toolbar.initActionStates)
[docs] def addAntibodyNumberingToolBar(self, area=QtCore.Qt.BottomToolBarArea): """ Add a `AntibodyNumberingToolBar` widget to the area indicated (default: bottom). :param area: The area to add the dock widget to """ toolbar = AntibodyNumberingToolBar(self.viewer) toolbar.setObjectName(self.ANTIBODY_NUM_TOOLBAR) self.addToolBar(area, toolbar)
[docs] def setAntibodyNumberingToolBarVisible(self, show=True): toolbar_name = self.ANTIBODY_NUM_TOOLBAR toolbar = self.findChild(QtWidgets.QToolBar, toolbar_name) if not toolbar: return if show: toolbar.showScheme() toolbar.show() else: toolbar.hideScheme() toolbar.hide()
[docs]class SimplifiedSequenceViewer(SequenceViewer): """ Creates a sequence viewer that can be opened by an action or added to a main window as a dockable item. Here is an example of how to add a dockable item to a QMainWindow, or AppFramework window:: from schrodinger.applications.bioluminate import sequence_viewer self.sequence_viewer = sequence_viewer.SimplifiedSequenceViewer(<window>) self.addDockWidget( QtCore.Qt.TopDockWidgetArea, self.sequence_viewer.asDock() ) If you want to add a button that will open the viewer in a new window:: from schrodinger.applications.bioluminate import sequence_viewer self.sequence_viewer = sequence_viewer.SimplifiedSequenceViewer(<window>) self.viewer_button = QtWidgets.QPushButton(self.tr('Open Model Viewer...')) self.viewer_button.clicked.connect(self.openSequenceViewer) then in the connected method (openSequenceViewer):: self.sequence_viewer.window.show() self.sequence_viewer.window.setFocus() If you want to simply add a sequence viewer frame to your app:: self.sequence_viewer = SimplifiedSequenceViewer(<window>) <layout>.addWidget( self.sequence_viewer.asFrame() ) """ WORKSPACE = 'workspace' PROJET_TABLE = 'projecttable' PDB_STRING = 'pdb_string' FILES = 'files' MANUAL_SEQUENCE = 'manual_sequence' # Custom signals need to be defined here for new style signals sequencesImported = QtCore.pyqtSignal(str) """ Signal emitted after any structures have been imported by any means into the viewer. The string passed in the emit will be one of: - `SimplifiedSequenceViewer.WORKSPACE` - `SimplifiedSequenceViewer.PROJECT_TABLE` - `SimplifiedSequenceViewer.PDB_STRING` - `SimplifiedSequenceViewer.FILES` """
[docs] def __init__(self, parent, toolbars=None, auto_align=False, require3d=True): """ :param parent: Parent widget of the sequence viewer :param toolbars: A list of toolbar flags to use (see `ViewerWindow` for available toolbars :type toolbars: list of strings :param auto_align: Whether to auto-align sequences when a new one is imported :type auto_align: boolean :param require3d: Whether to allow fasta sequence files to be imported. If set to True (default) only structures with 3D coordinates are allowed. :type require3d: boolean :see: `ViewerWindow` """ super(SimplifiedSequenceViewer, self).__init__(parent) # Set the toolbars to the ViewerWindows preset defaults # if they are not supplied if toolbars is None: toolbars = ViewerWindow.DEFAULT_TOOLBARS # Create the window and set it up self.window = ViewerWindow(self) self.window.addToolBars(toolbars) self.setupViewer() self.dock_widget = SequenceDockWidget() """ Widget the allows `self.window` to be dockable """ self.auto_synchronize = True self._auto_align = auto_align self._require3d = require3d self.last_sequences_imported = [] """ Stores the last sequences added to the viewer. """
def _setContextMenus(self): """ Private method to create the context menus used in the viewer. """ action_factory = Factory(self) # Create the "Name" context menus. These are the entries # on the left side of the sequence viewer. action_factory.setActions(action_config.VIEWER_NAME_CONTEXT) self.name_empty_menu = action_factory.getMenu() action_factory.setActions( action_config.VIEWER_NAME_CONTEXT, action_order=action_config.VIEWER_NAME_CONTEXT['annotate_order']) self.name_annotate_menu = action_factory.getMenu() action_factory.setActions( action_config.VIEWER_NAME_CONTEXT, action_order=action_config.VIEWER_NAME_CONTEXT['constraints_order']) self.name_constraints_menu = action_factory.getMenu() action_factory.setActions( action_config.VIEWER_NAME_CONTEXT, action_order=action_config.VIEWER_NAME_CONTEXT['aa_order']) self.name_aa_menu = action_factory.getMenu() # Create the "Sequence" context menu. These are the entries # on the right side of the sequence viewer. action_factory.setActions(action_config.VIEWER_SEQUENCE_CONTEXT) action_factory.addSubMenu(action_config.COLOR_SEQUENCES, add_separator=True, text='Color') action_factory.addSubMenu(action_config.ANNOTATE_ACTIONS, text='Annotate') self.sequence_menu = action_factory.getMenu()
[docs] def setupViewer(self): """ Sets up the viewer's window and context menus """ self._setContextMenus() # Set up the auto sync with Maestro # Commenting out since it is VERY SLOW #sv_maestro.initMaestro(self.sequence_group) # Please note the API changed and it should be called now: #self.initMaestro() self.setPopupMenus( self.nameContextCallback, self.sequenceContextCallback, )
[docs] def enableMaestroSync(self): """ Enables selection and color synchronization with Maestro """ self.initMaestro() self.auto_synchronize = True
[docs] def asDock(self): """ Returns the viewer as a dock widget. """ self.dock_widget.setWidget(self.window) return self.dock_widget
[docs] def asFrame(self): """ Returns the viewer as a frame widget """ return self.window
@property def protein_sequences(self): """ Property for all sequences in the viewer that are valid proteins :rtype: list of `sequences<schrodinger.ui.sequencealignment.sequence>` """ group = self.sequence_group sequences = [seq for seq in group.sequences if seq.isValidProtein()] return sequences @property def structure_sequences(self): """ Property for all sequences in the viewer that have structures. :rtype: list of `sequences<schrodinger.ui.sequencealignment.sequence>` """ group = self.sequence_group sequences = [seq for seq in group.sequences if seq.has_structure] return sequences @property def selected_sequences(self): """ Property returning all selected sequences in the viewer. This does not include a child sequence. :rtype: list of `sequences<schrodinger.ui.sequencealignment.sequence>` """ group = self.sequence_group selected = [seq for seq in group.sequences if seq.selected] return selected
[docs] def getReferenceStructure(self): """ Gets the structure associated with the reference sequence from maestro's PT. If there is no structure for the reference None is returned. :rtype: `schrodinger.structure.Structure` """ reference = self.sequence_group.reference return self.getSeqStructure(reference)
[docs] def generateSeqProjectRows(self, include_reference=True): """ Create a generator for all sequences that have a project table row associated with them. Yields each sequence's row from the PT. :param include_reference: Whether to include the reference seq :type include_reference: bool :returns: A generator that yields a `schrodinger.project.ProjectRow` :rtype: generator """ reference = self.sequence_group.reference pt = maestro.project_table_get() entry_ids = set(s.maestro_entry_id for s in self.structure_sequences) for entry_id in entry_ids: if not include_reference: # Use an entry_id comparison since sequences can be split # up by chains and the structure will be the same for chain # A and B if entry_id == reference.maestro_entry_id: continue row = pt.getRow(entry_id) yield row
[docs] def generateSeqStructures(self, include_reference=True): """ Create a generator for all sequences that have a project table row associated with them. Yields each sequence's structure from the PT. :param include_reference: Whether to include the reference structure :type include_reference: bool :returns: A generator that yields a `schrodinger.structure.Structure` :rtype: generator """ for row in self.generateSeqProjectRows(include_reference): yield row.getStructure()
[docs] def getSeqStructure(self, sequence): """ Get the structure associated with a `Sequence`. :returns: A structure object if found otherwise `None` :rtype: `None` or `structure<schrodinger.structure.Structure>` """ if sequence not in self.structure_sequences: return None else: pt = maestro.project_table_get() row = pt.getRow(sequence.maestro_entry_id) return row.getStructure()
[docs] @catch_last_added def importFromPDB(self, maestro_incorporate=True, multiple=True): """ Import structures from comma-separated list of PDB IDs. If `multiple=False` only the first PDB ID will be used. Returns None if user cancelled, or PDB ID(s) if not. """ if multiple: label = "Please enter a comma-separated list of PDBs to import:" else: label = 'Please enter a single PDB ID to import:' pdb_str, ok = QtWidgets.QInputDialog.getText(self, "Enter PDB ID", label) if not ok: return # User cancelled # Get the pdb ids, if multiple is False we only fetch # the first pdb id. pdb_ids = str(pdb_str) if not multiple: pdb_ids = [pdb for pdb in pdb_ids.split(',') if pdb.strip()] if not pdb_ids: pdb_ids = '' else: pdb_ids = pdb_ids[0] result = self.fetchSequence(pdb_ids, maestro_incorporate=maestro_incorporate) if result == "cancelled": return elif result == "error": msg = "Failed to retrieve PDB: %s" % pdb_ids msg += "\nCheck your internet connection and make sure CWD is writable" QtWidgets.QMessageBox.warning(self, "Error", msg) return if self._auto_align: if len(self.sequence_group.sequences) >= 1: self.runClustal() self.sequencesImported.emit(self.PDB_STRING) return pdb_ids
[docs] @catch_last_added def importFromFile(self, *args, **kwargs): """ Callback for import file action """ filters = kwargs.get('filter') if not filters: if self._require3d: filters = bwidgets.SequenceFileDialog.STRUCTURE_FILTERS else: filters = bwidgets.SequenceFileDialog.REFERENCE_FILTERS kwargs['filter'] = filters filenames = bwidgets.SequenceFileDialog.get_open_file_names( add_options=False, **kwargs) self.importFromFilePaths(filenames)
[docs] def importFromFilePaths(self, filenames, to_maestro=True): """ Import a list of filesnames into the sequence viewer. :param filenames: Filenames to be imported :type filenames: list of strings """ for filename in filenames: result = self.loadFile(str(filename), merge=False, replace=False, to_maestro=to_maestro, new_list=self.last_sequences_imported) # FIXME: Should we only do this for the imported sequences # or all of them. Right now its all of them. for seq in self.sequence_group.sequences: if seq.children: seq.hideChildren() self.generateRows() if self._auto_align: if len(self.sequence_group.sequences) >= 1: self.runClustal() if filenames: self.sequencesImported.emit(self.FILES)
[docs] @catch_last_added def importFromWorkspace(self, ignored=None): """ Import a structure from the workspace and load it into the sequence viewer. """ # FIXME: This should never happen. The GUI should take care of # disabling any option that brings us here if we are outside # Maestro. if maestro is None: return st = maestro.workspace_get() if len(st.atom) == 0: return # Load this into the viewer self.incorporateIncludedEntries() self.sequencesImported.emit(self.WORKSPACE)
[docs] @catch_last_added def importFromProjectTable(self): """ Import a structure from a selected PT row and load it into the sequence viewer. """ if maestro is None: return self.incorporateSelectedEntries() self.sequencesImported.emit(self.PROJECT_TABLE)
[docs] @catch_last_added def createSequence(self): """ Used to manually import a sequence into the viewer. We override the base class here so we can emit the sequencesImported signal. """ super(SimplifiedSequenceViewer, self).createSequence() self.sequencesImported.emit(self.MANUAL_SEQUENCE)
[docs] def addAnnotationAction(self): sender = self.sender() data = sender.data() #TODO: Add error catching here mode = int(data) super(SimplifiedSequenceViewer, self).addAnnotation(mode)
[docs] def setColorModeAction(self): sender = self.sender() data = sender.data() #TODO: Add error catching here mode = int(data) super(SimplifiedSequenceViewer, self).setColorMode(mode)
[docs] def focusFinder(self): """ Switch focus to Find Pattern input box. """ self.findToolBar.show() self.findToolBar.line_edit.setFocus()
[docs] def nameContextCallback(self, position, seq=None): """ Callback used when context menus called for the view with the pdb name. """ if not seq: self.name_empty_menu.popup(position) elif seq.type == sv_globals.SEQ_ANNOTATION: self.name_annotate_menu.popup(position) elif seq.type in [ sv_globals.SEQ_CONSTRAINTS, sv_globals.SEQ_QUERY_CONSTRAINTS ]: self.name_constraints_menu.popup(position) else: self.name_aa_menu.popup(position)
[docs] def sequenceContextCallback(self, position, res=None): """ Callback used when context menus called for the view with the sequences in it. """ if not res: return self.sequence_menu.popup(position)
[docs] def treeContextCallback(self, position, tree=None): """ Callback used when context menus called for the tree view """
# Do we need this?
[docs] def selectOnlyByEntry(self, entry_ids): """ Selects rows in the sequence viewer that are associated with the passed in entry ids. """ # First unselect all sequences self.sequence_group.unselectAllSequences() # Select the sequences passed in for seq in self.sequence_group.sequences: # Ignore sequences with entry_ids equal to None since # this method explicitly states that we are removing # sequences with entry ids. if seq.maestro_entry_id is None: continue if seq.maestro_entry_id in entry_ids: seq.selected = True self.updateView()
[docs] def selectOnlyBySeqs(self, sequences): """ Selects rows in the sequence viewer that are associated with the passed in sequences. :param sequences: Sequence or sequences to select in viewer :type sequences: `schrodinger.ui.sequencealignment.sequence.Sequence` or list of them. """ # First unselect all sequences self.sequence_group.unselectAllSequences() # Select the sequence passed in if type(sequences) in [list, tuple]: for seq in sequences: seq.selected = True else: sequences.selected = True self.updateView()
[docs] def deleteByEntry(self, entry_ids): """ Deletes rows in the sequence viewer that are associated with the passed in entry ids. If any rows are selected before this is called, they are retained. """ # Get the initially selected sequences selected = [] for seq in self.sequence_group.sequences: if seq.selected: selected.append(seq) # First select only the sequences you want to delete self.selectOnlyByEntry(entry_ids) # Delete the passed in sequences self.sequence_group.deleteSelected() # Reselect the initially selected sequences self.selectOnlyByEntry(selected)
[docs] def deleteBySequences(self, sequences): """ Deletes rows in the sequence viewer that are associated with the passed in sequences. If any rows are selected before this is called, they are retained. """ # Get the initially selected sequences selected = [] for seq in self.sequence_group.sequences: if seq.selected: selected.append(seq) # First select only the sequences you want to delete self.selectOnlyBySeqs(sequences) # Delete the passed in sequences self.sequence_group.deleteSelected() # Reselect the initially selected sequences self.selectOnlyByEntry(selected)
[docs] def importFromMaestro(self, method): ret = sv_maestro.maestroIncorporateEntries(self.sequence_group, what=method) # BIOLUM-1408 Set 'contents changed' flag self.contents_changed = True self.updateView() return ret
[docs] def closeWindow(self): """ Action for the "Close" button """ self.window.close()