Source code for schrodinger.trajectory.trajectory_gui_dir.advanced_settings

import textwrap
from enum import Enum

import schrodinger
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtGui
from schrodinger.Qt import QtWidgets
from schrodinger.structutils import analyze
from schrodinger.trajectory.trajectory_gui_dir.display_settings_data import \
    MAX_RADIUS
from schrodinger.trajectory.trajectory_gui_dir.display_settings_data import \
    MIN_RADIUS
from schrodinger.trajectory.trajectory_gui_dir.display_settings_data import \
    DisplayOnly
from schrodinger.trajectory.trajectory_gui_dir import playback_settings_data

from schrodinger.ui import maestro_ui
from schrodinger.ui.qt import atomselector

from . import advanced_settings_ui
from . import stylesheet

maestro = schrodinger.get_maestro()

WARNING_THRESHOLD_ATOMS = 1000000
ERROR_THRESHOLD_ATOMS = 5000000


[docs]class MessageType(Enum): """ Holds enums for error, warning, no issue cases """ NONE = 'none' ERROR = 'error' WARNING = 'warning'
[docs]class AdvancedSettings(QtWidgets.QDialog): """ Advanced settings class for trajectory viewer :cvar advancedSettingsDismissed(): A signal emitted after dismissing AdvancedSettings dialog. :vartype advancedSettingsDismissed: `QtCore.pyqtSignal` :cvar advancedSettingsChanged(): A signal emitted when advanced settings are changed. :vartype advancedSettingsChanged: `QtCore.pyqtSignal` """ advancedSettingsDismissed = QtCore.pyqtSignal() advancedSettingsChanged = QtCore.pyqtSignal()
[docs] def __init__(self, hide_hideatoms_related_settings, parent=None): """ :param hide_hideatoms_related_settings: A flag indicating whether to hide/show hide atoms related settings. :type hide_hideatoms_related_settings: bool :param parent: The Qt parent :type parent: QWidget """ super(QtWidgets.QDialog, self).__init__(parent) self.ui = advanced_settings_ui.Ui_Dialog() self.ui.setupUi(self) self.setStyleSheet(stylesheet.ADVANCED_SETTINGS_STYLESHEET) self.ui.cancel_pushbutton.clicked.connect(self.cancelClicked) self.ui.ok_pushbutton.clicked.connect(self.okClicked) self.ui.help_button.clicked.connect(self.help) self.atom_selector = atomselector.AtomSelector(self, show_pick=False, show_selection=False, show_plus=True) self.atom_selector.main_layout.setContentsMargins(2, 2, 2, 2) layout = self.ui.atomselector_widget.layout() layout.insertWidget(0, self.atom_selector) self.atom_selector.atomSelectionDialogDismissed.connect(self.show) self.atom_selector.atomSelectionDialogAboutToBeShown.connect(self.hide) self.ui.current_radiobutton.setChecked(True) self.ui.display_only_checkbox.setChecked(False) self.ui.display_only_frame.setEnabled(False) self.ui.display_only_checkbox.toggled.connect( self.ui.display_only_frame.setEnabled) self.ui.inc_lens_checkbox.setEnabled(False) self.ui.show_sim_checkbox.toggled.connect( self.ui.inc_lens_checkbox.setEnabled) self.ui.display_only_checkbox.toggled.connect( self.updateBidningSiteRadius) self.ui.current_radiobutton.toggled.connect( self.updateBidningSiteRadius) self.ui.display_only_checkbox.toggled.connect( self.updateAtomSelectorWidget) self.ui.matching_radiobutton.toggled.connect( self.updateAtomSelectorWidget) # Set range for replicate spinboxes replicate_spinboxes = ('x_spinbox', 'y_spinbox', 'z_spinbox') for spinbox in replicate_spinboxes: attr = getattr(self.ui, spinbox) attr.setRange( playback_settings_data.PlaybackSettingsData.MIN_REPLICATE, playback_settings_data.PlaybackSettingsData.MAX_REPLICATE) attr.valueChanged.connect(self.enableOK) # Set range for radius self.ui.radius_spinbox.setRange(MIN_RADIUS, MAX_RADIUS) # Enable 'OK' button when there is any change spinboxes = ('radius_spinbox',) + replicate_spinboxes for spinbox in spinboxes: attr = getattr(self.ui, spinbox) attr.valueChanged.connect(self.enableOK) abstractbuttons = ('display_only_checkbox', 'current_radiobutton', 'matching_radiobutton', 'sec_struct_checkbox', 'show_sim_checkbox', 'load_sel_pushbutton', 'inc_lens_checkbox') for abstractbutton in abstractbuttons: attr = getattr(self.ui, abstractbutton) attr.clicked.connect(self.enableOK) self.atom_selector.aslTextModified.connect(self.enableOK) self.ui.load_sel_pushbutton.clicked.connect( self.atom_selector.loadWorkspaceSelection) if hide_hideatoms_related_settings: self.ui.define_widget.setVisible(False) self.ui.hideatoms_label.setVisible(False) self.adjustSize() self.data = None
[docs] def setPlaybackSettingsData(self, settings_data, has_pbsettings_data, fsys_atom_total=0): """ Set the playback settings data to the current entry trajectory. If the entry trajectory has saved placback settings, the same will be used to show in UI. Otherwise, populate the latest values from UI and override the values in settings_data :param settings_data: Playback settings data stored in PlaybackSettings object :type settings_data: PlaybackSettingsData Object :param has_pbsetetings_data: True if the current entry trajectory has saved playback setings data :type has_pbsettings_data: bool :param fsys_atom_total: Total number of atoms in the system :type fsys_atom_total: int """ showing_first_time = self.data is None self.data = settings_data self.fsys_ct_atom_total = fsys_atom_total if not has_pbsettings_data and not showing_first_time: self.data.binding_site_radius = self.ui.radius_spinbox.value() self.data.display_only = self.ui.display_only_checkbox.isChecked() self.data.display_only_option = DisplayOnly.MATCHING if self.ui.matching_radiobutton.isChecked( ) else DisplayOnly.CURRENT self.data.update_secondary_structure = self.ui.sec_struct_checkbox.isChecked( ) self.data.show_simulation_box = self.ui.show_sim_checkbox.isChecked( ) self.data.include_vector_lengths = self.ui.inc_lens_checkbox.isChecked( ) self.data.replicate_x = self.ui.x_spinbox.value() self.data.replicate_y = self.ui.y_spinbox.value() self.data.replicate_z = self.ui.z_spinbox.value() self.data.matching_asl = self.atom_selector.getAsl()
[docs] def showDlg(self): """ Update the dialog and post it. """ self.updateDlg() self.ui.ok_pushbutton.setEnabled(False) self.show()
[docs] def setReplicateErrorWarningIcon(self, message_type: MessageType): """ Set the icon and the text for warning and error scenarios """ if message_type == MessageType.WARNING: self.updateStatusLabel( "May cause slowness", MessageType.WARNING, textwrap.dedent(""" Replication will exceed 1 million atoms. Maestro may become unusably slow. """)) elif message_type == MessageType.ERROR: self.updateStatusLabel( "Too many atoms", MessageType.ERROR, "Replication cannot exceed 5 million atoms.")
[docs] def updateStatusLabel(self, title: str, message_type: MessageType, tooltip: str): """ Update status label associated with replication components. """ style = self.style() if message_type == MessageType.NONE: self.ui.status_label_icon.setPixmap(QtGui.QPixmap("")) self.ui.status_label_icon.setToolTip("") else: icon = style.standardIcon( style.SP_MessageBoxWarning if message_type == MessageType.WARNING else style.SP_MessageBoxCritical) pixmap = icon.pixmap(15, 15) self.ui.status_label_icon.setPixmap(pixmap) self.ui.status_label_icon.setToolTip(tooltip) self.ui.status_label_title.setMinimumHeight( self.ui.status_label_icon.height()) self.ui.status_label_title.setProperty('errtype', message_type.value) self.ui.status_label_title.setText(title) self.ui.status_label_title.setToolTip(tooltip) self.ui.status_label_title.style().unpolish(self.ui.status_label_title) self.ui.status_label_title.style().polish(self.ui.status_label_title)
[docs] def updateReplicatedAtomTotalStatus(self): """ Check the current atom total based on the replication settings """ enable_ok = True frame_ct_atom_total = self.fsys_ct_atom_total x_replicate = self.ui.x_spinbox.value() y_replicate = self.ui.y_spinbox.value() z_replicate = self.ui.z_spinbox.value() replicated_atom_total = frame_ct_atom_total * x_replicate * y_replicate * z_replicate self.ui.status_label_title.setText("") self.ui.status_label_title.setToolTip("") self.ui.status_label_icon.setPixmap(QtGui.QPixmap("")) # Show the error message if the replicated atom total exceeds 5 million atoms if (replicated_atom_total >= ERROR_THRESHOLD_ATOMS): enable_ok = False self.setReplicateErrorWarningIcon(MessageType.ERROR) # Show the warning message if the replicated atom total exceeds 1 million atoms elif (replicated_atom_total >= WARNING_THRESHOLD_ATOMS): self.setReplicateErrorWarningIcon(MessageType.WARNING) return enable_ok
[docs] def enableOK(self): """ Enable 'OK' button """ asl_enable_ok = True # Do not enable okay button if 'Display Only:' is toggled on, and # 'Atoms matching ASL definition:' is checked and asl in # atom selector empty asl_enable_ok = not ((self.ui.display_only_checkbox.isChecked()) and (self.ui.matching_radiobutton.isChecked()) and (not self.atom_selector.getAsl())) replicated_atom_count_ok = self.updateReplicatedAtomTotalStatus() self.ui.ok_pushbutton.setEnabled(asl_enable_ok and replicated_atom_count_ok)
[docs] def isDisplayOnlyCurrentDisplayedAtoms(self): """ Whether to display only currently displayed atoms """ return self.ui.display_only_checkbox.isChecked( ) and self.ui.current_radiobutton.isChecked()
[docs] def updateBidningSiteRadius(self): """ Set enable state of 'Define binding site radius as:'. """ self.ui.define_widget.setEnabled( not self.isDisplayOnlyCurrentDisplayedAtoms())
[docs] def updateAtomSelectorWidget(self): """ Set enable state of atom selector widget """ self.ui.atomselector_widget.setEnabled( (self.ui.display_only_checkbox.isChecked()) and (self.ui.matching_radiobutton.isChecked()))
[docs] def updateDlg(self): """ Update dialog according to data """ self.ui.radius_spinbox.setValue(self.data.binding_site_radius) self.ui.display_only_checkbox.setChecked(self.data.display_only) if self.data.display_only_option == DisplayOnly.CURRENT: self.ui.current_radiobutton.setChecked(True) else: self.ui.matching_radiobutton.setChecked(True) self.ui.sec_struct_checkbox.setChecked( self.data.update_secondary_structure) self.ui.show_sim_checkbox.setChecked(self.data.show_simulation_box) self.ui.inc_lens_checkbox.setChecked(self.data.include_vector_lengths) self.ui.x_spinbox.setValue(self.data.replicate_x) self.ui.y_spinbox.setValue(self.data.replicate_y) self.ui.z_spinbox.setValue(self.data.replicate_z) allow_replication = self.data.allow_replication for w in (self.ui.x_spinbox, self.ui.y_spinbox, self.ui.z_spinbox): w.setEnabled(allow_replication) self.atom_selector.setAsl(self.data.matching_asl) ws_hub = maestro_ui.WorkspaceHub.instance() self.ui.load_sel_pushbutton.setEnabled( ws_hub.getSelectedAtomCount() > 0) if allow_replication: self.updateReplicatedAtomTotalStatus() else: self.updateStatusLabel(title="Workspace not replicable", message_type=MessageType.NONE, tooltip=textwrap.dedent(""" The trajectory cannot be replicated after a second entry has been included. To play a trajectory with replication while viewing another entry, request the replication immediately after the trajectory entry is included in the Workspace. """))
[docs] def cancelClicked(self): """ Reject and emit advancedSettingsDismissed() signal when clicked on 'Cancel' button """ self.close() self.advancedSettingsDismissed.emit()
[docs] def isValidASL(self, asl): """ Return True if asl in the text box is valid. """ return analyze.validate_asl(asl)
[docs] def okClicked(self): """ Update data according to dialog """ # Warn user in case of invalid asl. matching_asl_selected = (self.ui.display_only_checkbox.isChecked() and self.ui.matching_radiobutton.isChecked()) matching_asl = self.atom_selector.getAsl() if (matching_asl_selected and (not self.isValidASL(matching_asl))): maestro.warning(f"Invalid ASL {matching_asl}") return self.close() self.data.binding_site_radius = self.ui.radius_spinbox.value() self.data.display_only = self.ui.display_only_checkbox.isChecked() display_only_option = DisplayOnly.CURRENT if self.ui.matching_radiobutton.isChecked(): display_only_option = DisplayOnly.MATCHING self.data.display_only_option = display_only_option self.data.update_secondary_structure = self.ui.sec_struct_checkbox.isChecked( ) self.data.show_simulation_box = self.ui.show_sim_checkbox.isChecked() self.data.include_vector_lengths = self.ui.inc_lens_checkbox.isChecked() self.data.replicate_x = self.ui.x_spinbox.value() self.data.replicate_y = self.ui.y_spinbox.value() self.data.replicate_z = self.ui.z_spinbox.value() self.data.matching_asl = self.atom_selector.getAsl() self.advancedSettingsChanged.emit() self.advancedSettingsDismissed.emit()
[docs] def help(self): """ Shows 'Advanced Playback Settings' dialog help """ if maestro: maestro.command("helptopic TRAJECTORY_ADVANCED_PLAYBACK_SETTINGS") maestro.command("showpanel help")