import textwrap
import schrodinger
from schrodinger import structure
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtGui
from schrodinger.Qt import QtWidgets
from schrodinger.Qt.QtCore import Qt
from schrodinger.trajectory.trajectory_gui_dir import advanced_settings
from schrodinger.trajectory.trajectory_gui_dir import frame_structure_exporter
from schrodinger.trajectory.trajectory_gui_dir import \
snapshot_custom_display_settings
from schrodinger.trajectory.trajectory_gui_dir.export_structure_enums import \
ExportMode
from schrodinger.trajectory.trajectory_gui_dir import trajectory_messages
from schrodinger.ui import maestro_ui
from schrodinger.ui.qt.appframework2 import af2
from . import snapshot_panel_ui
from . import stylesheet
maestro = schrodinger.get_maestro()
# Initial width of the panel (in pixels)
INITIAL_WIDTH = 320
[docs]class FrameListItem(QtWidgets.QListWidgetItem):
"""
Frame list item is checkable, selectable, and enabled.
"""
[docs] def __init__(self, value: int):
"""
Constructor.
:param value: The frame number representing this list widget item.
"""
super().__init__(str(value))
self.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable |
Qt.ItemNeverHasChildren | Qt.ItemIsEnabled)
self.setToolTip(f"Show/Hide trajectory frame {value} in the workspace.")
# Default is always keep checked.
self.setCheckState(Qt.Checked)
@property
def visible(self):
"""
:rtype: bool
:return: True if frame item is visible.
"""
return self.checkState() == Qt.Checked
@property
def frame_number(self):
"""
:rtype: int
:return: The frame number representing this list widget item.
"""
return int(self.text())
def __lt__(self, other):
"""
Overridden base class __lt__ function.
:type: FrameListItem
:param other: Other frame list item to be compared with self.
:rtype: bool
:return: True if this item is less than other item.
"""
return self.frame_number < other.frame_number
def __gt__(self, other):
"""
Overridden base class __gt__ function.
:type: FrameListItem
:param other: Other frame list item to be compared with self.
:rtype: bool
:return: True if this item is greater than other item.
"""
return self.frame_number > other.frame_number
[docs]class FrameRangeValidator(QtGui.QRegularExpressionValidator):
"""
Frame range validator.
Valid values are like this --
1
1,2,3
1,2,3-5,5
3-5
1-5,3,5,6-7
Validator also ensures that maximum number is within the range of maximum
possible allowed value.
"""
LIST_SEPARATOR = ','
RANGE_SEPARATOR = '-'
[docs] def __init__(self, parent=None):
"""
Construct frame range validator object.
:type parent: QtWidgets.QWidget
:param parent: Parent widget.
"""
super().__init__(parent)
self._max_frame_value = None
self.setRegularExpression(
QtCore.QRegularExpression("^[1-9]?[0-9,,,-]*[0-9]$"))
self.invalid_tokens = [
self.RANGE_SEPARATOR * 2, self.LIST_SEPARATOR * 2,
self.RANGE_SEPARATOR + self.LIST_SEPARATOR,
self.LIST_SEPARATOR + self.RANGE_SEPARATOR
]
@property
def max_frame_value(self):
"""
:rtype: int
:return: Return maximum possible frame value.
"""
return self._max_frame_value
[docs] def setMaxFrame(self, max_frame_value: int):
"""
Set maximum frame number allowed.
:param max_frame_value: Maximum allowed value to be used by validator
for validation.
"""
self._max_frame_value = max_frame_value
[docs] def validate(self, in_str: str, pos: int):
"""
Provides a range checking of frame numbers.
:param in_str: Input string entered by user.
:param pos: Cursor position in the input editor.
:rtype: enum(QtGui.QValidator.State)
:return: State defined by QtGui.QValidator.State enum.
"""
state, in_str, pos = super().validate(in_str, pos)
in_str_len = len(in_str)
# If empty string or invalid character, return whatever Qt validated.
if (not in_str) or state == QtGui.QValidator.Invalid:
return (state, in_str, pos)
# Starting with range or list separator is not allowed.
if in_str[0] in [self.RANGE_SEPARATOR, self.LIST_SEPARATOR]:
return (QtGui.QValidator.Invalid, in_str, pos)
elif self._containsInvalidToken(in_str):
return (QtGui.QValidator.Invalid, in_str, pos)
for token in self.getTokens(in_str):
if self.isRangeToken(token):
frame_numbers = self.getRange(token)
else:
frame_numbers = [int(token)]
if frame_numbers is None:
return (QtGui.QValidator.Invalid, in_str, pos)
for frame_number in frame_numbers:
if frame_number > self.max_frame_value or frame_number <= 0:
return (QtGui.QValidator.Invalid, in_str, pos)
return (state, in_str, pos)
def _containsInvalidToken(self, expr: str):
"""
Return True if expr contains any invalid token listed from invalid_tokens list
:param expr: Input expression to be verified.
:rtype: bool
:return: True if expression contains any invalid token.
"""
for token in self.invalid_tokens:
if expr.find(token) != -1:
return True
return False
[docs] def getRange(self, token: str):
"""
Return a tuple of number based on range token.
:param token: Token to be parsed to get range.
:rtype: tuple(int, int) or None
:return: Return a tuple of start and end number. If pattern is 1-5-6,
then it is not allowed and returns None.
"""
if token.count(self.RANGE_SEPARATOR) == 1:
return tuple(
[int(v) for v in token.split(self.RANGE_SEPARATOR) if v])
[docs] def isRangeToken(self, token: str):
"""
Return True if token is a range token (e.g. '5-10')
:param token: Token to be checked if it is range token or not.
:rtype: bool
:return: True if token is a range token. If token value is '1-',
'1-5-6' then we consider that as a valid range token because it is
likely user is about to edit the text in the text edit box.
"""
return self.RANGE_SEPARATOR in token
[docs] def getTokens(self, expr: str):
"""
Split text and return tokens which would be either number or x-y form.
:param: Frame range or list expression.
:rtype: list(str)
:return: List of frame number or frame range tokens.
"""
return [v for v in expr.split(self.LIST_SEPARATOR) if v]
[docs]class SnapshotPanel(af2.App):
"""
Trajectory snapshot panel class for trajectory frames viewing.
It provides ability to display multiple frames in the workspace
from a single trajectory.
:cvar modeChanged: A signal emitted when panel activates frame
snapshots viewing. True if snapshot mode activated, otherwise False.
:vartype modeChanged: QtCore.pyqtSignal
:cvar closed: A signal emitted when panel is closed.
:vartype closed: QtCore.pyqtSignal
:cvar visiblityChanged: A signal emitted when panel is displayed or
hidden. - True if snapshot panel is visible, otherwise False.
:vartype visibilityChanged: QtCore.pyqtSignal
:cvar atomsColorChanged: A signal emitted when atoms color changes as a
result of turning on show color gradient option.
:vartype atomsColorChanged: QtCore.pyqtSignal
atomsColorChanged emitted when:
- Workspace structure which is updated.
- Change type
- Notify about change
- Notify about id map (always -1)
- Notify if only bonds changed(always False)
"""
DEFAULT_STEP_SIZE = 10
MIN_STEP_SIZE = 1
modeChanged = QtCore.pyqtSignal(bool)
closed = QtCore.pyqtSignal()
visibilityChanged = QtCore.pyqtSignal(bool)
atomsColorChanged = QtCore.pyqtSignal(int,
maestro_ui.MMENUM_DGO_CHANGED_TYPE,
bool, int, bool)
[docs] def setPanelOptions(self):
super().setPanelOptions()
self.ui = snapshot_panel_ui.Ui_snapshot_panel()
self.title = "Display Trajectory Snapshots"
self.help_topic = 'TRAJECTORY_DISPLAY_SNAPSHOTS'
self.add_main_layout_stretch = False
self.maestro_dockable = True
self.dock_area = Qt.RightDockWidgetArea
self.setObjectName("snapshot_panel_docking_widget")
[docs] def __init__(self, player, parent=None):
"""
:type player: playertoolbar.TrajectoryPlayer
:param player: Player toolbar
:type parent: QtWidgets.QWidget
:param parent: Parent widget.
"""
self._player = player
self._resetData()
super().__init__(parent=parent)
[docs] def setup(self):
super().setup()
self.setFixedWidth(INITIAL_WIDTH)
self._setDefaults()
self.setStyleSheet(stylesheet.SNAPSHOT_PANEL_STYLESHEET)
self._setupFramesOption()
self._setupSettingsOption()
self.ui.frame_listw.itemSelectionChanged.connect(
self._updateFrameListAndDependentButtons)
self.ui.frame_listw.itemChanged.connect(self._updateWorkspace)
self.ui.export_button.clicked.connect(self._exportFrames)
self.ui.delete_selected_pushb.clicked.connect(self._deleteSelected)
self.ui.delete_all_pushb.clicked.connect(self._deleteAll)
self.ui.display_snapshots_toolbutton.toggled.connect(
self._toggleSnapshots)
self.adjustSize()
self.specified_frame_def_label = self.ui.specific_frame_numbers_radiob.text(
)[0:-1]
self._structure_exporter = frame_structure_exporter.FrameStructureExporter(
player_obj=self._player,
export_mode=ExportMode.SNAPSHOT_VIEWER,
parent=self)
self.ui.info_button.setToolTip(
textwrap.dedent("""
View positioning/alignment and other features of the display
are based on the Trajectory Player's Playback Settings. Only
atom visibility may be adjusted here.
"""))
self._custom_settings_dlg = snapshot_custom_display_settings.CustomDisplaySettings(
self)
self._custom_settings_dlg.settingsChanged.connect(
self._customSettingsChanged)
def _resetData(self):
"""
Reset panel data state to default.
"""
self._frame_ct = None
self._currently_displayed_frames = None
def _customSettingsChanged(self):
"""
This slot gets called when user is changing custom settings.
As a result of custom settings changes, we need to update frames
snapshot in the workspace.
"""
self._settings_change_handled = True
self._updateWorkspace(settings_changed=True)
def _updateTrajectoryDependentOptions(self):
"""
Update panel using entry trajectory data.
"""
total_frame = self._player.total_frame if self._player else 0
self._step_validator.setRange(self.MIN_STEP_SIZE, total_frame)
self.cooltowarmcolor_map = maestro_ui.CoolToWarmColorMap(
self._player.getFrameToActiveAtomTotalMap())
self._updateSpecifiedFrameRangeLimit(total_frame)
self._validateAndUpdateStep(total_frame)
self._validateAndRomoveInvalidFrameItems(total_frame)
def _updateSpecifiedFrameRangeLimit(self, total_frame: int):
"""
Update specified frame range radio button label text
and validator range.
"""
self._frame_range_validator.setMaxFrame(total_frame)
range_label = self.specified_frame_def_label
if total_frame > 0:
range_label += f' (1-{total_frame})'
range_label += ':'
self.ui.specific_frame_numbers_radiob.setText(range_label)
def _updateFrameListAndDependentButtons(self):
"""
Update list widget and its dependent buttons
(Delete, Delete All, Export..., and Display Snapshots)
"""
has_frames = self.list_widget_item_count > 0
for obj in [
self.ui.display_snapshots_toolbutton, self.ui.delete_all_pushb,
self.ui.export_button
]:
obj.setEnabled(has_frames)
self.ui.delete_selected_pushb.setEnabled(
has_frames and len(self.ui.frame_listw.selectedItems()) > 0)
def _getStep(self):
"""
Return current step size.
"""
value = self.ui.step_number_line_edit.text()
return int(value) if value else 0
def _validateAndUpdateStep(self, total_frame: int):
"""
Validate and update step size.
"""
current_step = self._getStep()
if current_step and current_step < total_frame:
self.ui.step_number_line_edit.setText(str(current_step))
else:
default_step = self._getDefaultStepSize()
self.ui.step_number_line_edit.setText(str(default_step))
def _getDefaultStepSize(self):
"""
Return default step size.
"""
if self._player and self._player.step_size > self.MIN_STEP_SIZE:
return self._player.step_size
else:
return self.DEFAULT_STEP_SIZE
def _setDefaults(self):
"""
Set default values for the panel.
"""
self.ui.current_workspace_frame_radiob.setChecked(True)
# By default hide change settings label.
self._custom_settings_defined = False
self._settings_change_handled = False
self.ui.change_settings_link.setVisible(False)
self.ui.frame_number_line_edit.clear()
self.ui.apply_time_based_gradient_checkb.setChecked(True)
self.ui.show_hide_atoms_checkb.setChecked(True)
self.ui.trajectory_player_settings_radiob.setChecked(True)
def _setupFramesOption(self):
"""
Setup frame option buttons and their connection.
"""
self.ui.frame_buttongroup.buttonClicked.connect(
self._updateFramesOption)
self._step_validator = QtGui.QIntValidator(
self.ui.step_number_line_edit)
self.ui.step_number_line_edit.setValidator(self._step_validator)
self.ui.step_number_line_edit.setClearButtonEnabled(True)
self.ui.step_number_line_edit.textChanged.connect(
self._updateAddSnapshotButton)
self.ui.add_snapshots_pushb.clicked.connect(self._addSnapshotFrames)
self.ui.frame_number_line_edit.setClearButtonEnabled(True)
self.ui.frame_number_line_edit.textChanged.connect(
self._updateAddSnapshotButton)
self._frame_range_validator = FrameRangeValidator(
self.ui.frame_number_line_edit)
self.ui.frame_number_line_edit.setValidator(self._frame_range_validator)
def _toggleSnapshots(self, state: bool):
"""
Enter or exit in snapshot mode.
"""
if state:
self.enterDisplaySnapshotMode()
else:
self.exitDisplaySnapshotMode()
def _clearWorkspace(self):
"""
Delete all atoms from workspace ct.
"""
maestro.command(
"delete all allowemptyentry=true showemptyentrywarning=false")
def _needWorkspaceUpdate(self, settings_changed: bool, frames: list):
"""
:param settings_changed: True if there was setting change.
:param frames: The list of displayed frames.
:return: True if there is a need to update workspace.
"""
if not self.is_display_snapshot_active:
return False
# If nothing changed, then noop.
if frames == self._currently_displayed_frames and (
not settings_changed):
return False
return True
def _updateWorkspace(self, settings_changed=False):
"""
Update workspace using frames opted by user to display.
"""
frames = self._getCurrentFramesToDisplay()
if not self._needWorkspaceUpdate(frames, settings_changed):
return
self._currently_displayed_frames = frames
# If there is no frame to display, we should just delete all atoms from
# the current workspace ct.
if not frames:
self._clearWorkspace()
return
# Create clean ct which we will prepare using frames selected for
# display in the workspace.
merged_ct = structure.create_new_structure(0)
forced_update = True
varying_atoms_asl = self._player.isVaryingAtomsFrame()
ref_ct = self._player.getOriginalEntryCT(copy=False)
dsm = self._custom_settings_dlg.display_settings_manager
# If we are using custom settings, then always ensure that first time we
# apply complete asl.
if self.useCustomSettings():
dsm.setDisplayAtomsASL()
# We should restore default visibility if user is not using both custom or
# trajectory display settings.
if not self.useShowHideAtomsAsl():
dsm.restoreDefaultVisibility(ref_ct, self._frame_ct)
for frame in frames:
updated_frame_ct = self._player.getUpdatedFrameSnapshot(
frame, self._frame_ct, forced_update,
self.useTrajectorySettings())
if self.useCustomSettings():
if dsm.shouldRestoreVisibility(varying_atoms_asl):
dsm.restoreDefaultVisibility(ref_ct, updated_frame_ct)
dsm.applyShowHideAtomsAsl(updated_frame_ct, varying_atoms_asl)
forced_update = False
merged_ct.extend(updated_frame_ct)
if self.isApplyTimeBasedGradient():
self._applyTimeBasedGradientColor(merged_ct)
maestro.workspace_set(merged_ct, check_scratch_entry=False, copy=False)
maestro.command("fit all")
[docs] def isApplyTimeBasedGradient(self):
"""
:return: True if show time based color gradient option checked.
"""
return self.ui.apply_time_based_gradient_checkb.isChecked()
[docs] def useShowHideAtomsAsl(self):
"""
:return: True if show/hide atoms asl should be applied.
:rtype: bool
"""
return self.ui.show_hide_atoms_checkb.isChecked()
[docs] def useTrajectorySettings(self):
"""
:return: True if trajectory settings should be used.
:rtype: bool
"""
return self.useShowHideAtomsAsl(
) and self.ui.trajectory_player_settings_radiob.isChecked()
[docs] def useCustomSettings(self):
"""
:return: True if custom settings should be used.
:rtype: bool
"""
return self.useShowHideAtomsAsl(
) and self.ui.custom_settings_radiob.isChecked()
def _setupSettingsOption(self):
"""
Setup settings option buttons and their connection.
"""
self.ui.settings_buttongroup.buttonClicked.connect(
self._atomsVisiblitySettingOptionChanged)
self.ui.show_hide_atoms_checkb.toggled.connect(
self._showHideOptionToggled)
self.ui.apply_time_based_gradient_checkb.toggled.connect(
self._applyTimeBasedGradientToggled)
self.ui.change_settings_link.clicked.connect(
self._showCustomSettingsDlg)
def _showCustomSettingsDlg(self):
"""
Show custom settings dialog.
"""
self._custom_settings_dlg.showDlg()
self._custom_settings_defined = True
def _applyTimeBasedGradientToggled(self, state: bool):
"""
Slot which gets called when user clicks on apply time based gradient
toggle button. Workspace frames are updated according to user selected
option.
"""
if not self._currently_displayed_frames:
return
frames = self._currently_displayed_frames
# In case if snapshot mode is not active or there is no frame to
# display, there is nothing to do.
if not self._needWorkspaceUpdate(settings_changed=True, frames=frames):
return
# We don't track default color of atoms and ribbon, so we need to
# regenerate all frames.
if state == False:
self._updateWorkspace(settings_changed=True)
# It is enough to change the color.
else:
st = maestro.workspace_get(copy=False)
self._applyTimeBasedGradientColor(st)
self.atomsColorChanged.emit(st.handle,
maestro_ui.MM_DGO_COLOR_CHANGED, True,
-1, False)
def _applyTimeBasedGradientColor(self, st: structure.Structure):
"""
Apply time based gradient color.
:param st: Workspace strucutre to be updated by color.
"""
self.cooltowarmcolor_map.applyColors(st.handle,
self._currently_displayed_frames)
def _showHideOptionToggled(self, state: int):
"""
Slot which gets called when user clicks on show/hide toggle button.
"""
for obj in [
self.ui.trajectory_player_settings_radiob,
self.ui.custom_settings_radiob, self.ui.change_settings_link
]:
obj.setEnabled(state)
self._updateSettingsOption()
self._updateWorkspace(settings_changed=True)
def _atomsVisiblitySettingOptionChanged(self):
"""
Slot which gets called when user is switching between custom setting or
player settings options.
It updates workspace.
"""
self._updateSettingsOption()
self._updateWorkspace(settings_changed=True)
[docs] def updatePanel(self):
"""
Update panels component based on current selected options.
"""
self.setEnabled(True)
self._updateTrajectoryDependentOptions()
self._updateFramesOption()
self._updateSettingsOption()
self.updateCurrentFrameLabel()
self._updateFrameListAndDependentButtons()
def _validateAndRomoveInvalidFrameItems(self, total_frame: int):
"""
Validate frame list and accordingly remove items from the list widget.
:param total_frame: Total number of frames in the trajectory.
"""
frame_listw = self.ui.frame_listw
items_count = self.list_widget_item_count
invalid_frame_items = []
for frame in range(0, items_count):
frame_item = frame_listw.item(frame)
if frame_item.frame_number > total_frame:
invalid_frame_items.append(frame_item)
self._deleteFrameItems(invalid_frame_items)
def _updateFramesOption(self):
"""
Update frame options - enable/disable components appropriately.
"""
step_option_checked = self.ui.stepwise_radio_button.isChecked()
frame_option_checked = self.ui.specific_frame_numbers_radiob.isChecked()
self.ui.step_number_line_edit.setEnabled(step_option_checked)
self.ui.frame_number_line_edit.setEnabled(frame_option_checked)
self.ui.step_number_line_edit.setVisible(step_option_checked)
self.ui.frame_number_line_edit.setVisible(frame_option_checked)
self.ui.add_snapshots_pushb.setText(
"Add Snapshots" if step_option_checked else "Add Snapshot")
self._updateAddSnapshotButton()
def _updateAddSnapshotButton(self):
"""
Enable/disable Add Snapshot button.
"""
self.ui.add_snapshots_pushb.setEnabled(self._shouldEnableAddSnapshot())
@property
def is_display_snapshot_active(self):
"""
:rtype: bool
:return: True if display snapshot mode button is turned on.
"""
return self.ui.display_snapshots_toolbutton.isChecked()
[docs] def closeEvent(self, close_event: QtGui.QCloseEvent):
"""
Overridden base class method to ensure that panel exits from snapshot
model if it was in that mode.
:param close_event: Qt close event.
"""
# Turn of Display snapshot mode button which will automatically cause
# panel to exit from snapshot mode.
if self.is_display_snapshot_active:
self.ui.display_snapshots_toolbutton.setChecked(False)
super().closeEvent(close_event)
self.closed.emit()
[docs] def showEvent(self, show_event: QtGui.QShowEvent):
"""
Overridden base class method to ensure that panel notifies its
visibility state change.
:param show_event: Qt show event.
"""
super().showEvent(show_event)
self.visibilityChanged.emit(True)
[docs] def hideEvent(self, hide_event: QtGui.QHideEvent):
"""
Overridden base class method to ensure that panel notifies its
visibility state change.
:param hide_event: Qt hide event.
"""
super().hideEvent(hide_event)
self.visibilityChanged.emit(False)
[docs] def exitDisplaySnapshotMode(self):
"""
Exit from display snapshot mode.
Enable player toolbar.
"""
self.modeChanged.emit(False)
self._resetData()
[docs] def enterDisplaySnapshotMode(self):
"""
Slot which gets called when user clicks on add snapshot button.
It builds a structure based on selected frames and settings.
Disable player toolbar.
"""
frames = self._getCurrentFramesToDisplay()
num_atoms = self._getCurrentFramesToDisplayAtomTotal(frames)
if num_atoms > advanced_settings.WARNING_THRESHOLD_ATOMS:
num_frames = len(frames)
if not trajectory_messages.show_snapshot_atom_limit_warning(
num_frames, num_atoms, self):
self.ui.display_snapshots_toolbutton.setChecked(False)
return
self.modeChanged.emit(True)
self._frame_ct = self._player.getOriginalEntryCT(copy=True)
self._updateWorkspace()
def _getCurrentFramesToDisplayAtomTotal(self, frames: list) -> int:
"""
Return number of total atoms to dispaly for current frames.
:type frame: list(int)
:param frames: List of current frames.
"""
atom_total_map = self._player.getFrameToActiveAtomTotalMap()
return sum(atom_total_map[frame - 1] for frame in frames)
def _shouldEnableAddSnapshot(self):
"""
Return True if add snapshot button should be enabled.
"""
if self.ui.specific_frame_numbers_radiob.isChecked():
specified_frames_text = self._getSpecifiedFramesText()
return (self._frame_range_validator.validate(
specified_frames_text,
len(specified_frames_text))[0] == QtGui.QValidator.Acceptable)
elif self.ui.stepwise_radio_button.isChecked():
return self._getStep() > 0
else:
return self._getCurrentFrame() > 0
def _getSpecifiedFramesText(self):
"""
Return text entered in the specified frame line edit text box.
"""
return self.ui.frame_number_line_edit.text()
def _getCurrentFrame(self):
"""
Return current frame number in the player toolbar, otherwise returns 0.
"""
return self._player.current_frame_number if self._player else 0
[docs] def setEnabledCurrentFrameOptions(self, enable: bool):
"""
Enable/disable current workspace frame option based on enable flag.
"""
self.ui.current_workspace_frame_radiob.setEnabled(enable)
self.ui.current_frame_label.setEnabled(enable)
# If current frame option is checked and it is being disabled,
# then automatically select next option.
if not enable and self.ui.current_workspace_frame_radiob.isChecked():
self.ui.stepwise_radio_button.setChecked(True)
self._updateFramesOption()
[docs] def updateCurrentFrameLabel(self):
"""
Update current frame number label.
"""
frame_number = self._getCurrentFrame()
self.ui.current_frame_label.setText(f'(Frame {frame_number})')
def _updateSettingsOption(self):
"""
Update settings options.
"""
if self.ui.custom_settings_radiob.isChecked():
# If user never defined custom settings, then automatically
# display custom settings dialog first time, subsequently, we will
# show dialog only if user clicks on Change link.
if not self._custom_settings_defined:
self._showCustomSettingsDlg()
# First time, we have to explicitly trigger settings changed
# because it is possible user opened dialog and accepted
# default values, so we won't get settingsChanged signal.
if not self._settings_change_handled:
self._customSettingsChanged()
self.ui.change_settings_link.setVisible(
self._custom_settings_defined)
elif self.ui.trajectory_player_settings_radiob.isChecked():
self.ui.change_settings_link.setVisible(False)
def _exportFrames(self):
"""
Display export frames dialog and export frames accordingly.
"""
dsm = self._custom_settings_dlg.display_settings_manager
selected_frames = [
frame_item.frame_number
for frame_item in self.ui.frame_listw.selectedItems()
]
selected_frames.sort()
current_frames = list(self._getCurrentFramesSet())
current_frames.sort()
# If we are using custom settings, then always ensure that first time we
# apply complete asl.
if self.useCustomSettings():
dsm.setDisplayAtomsASL()
self._structure_exporter.showExportSnapshotStructuresDlg(
current_frames, selected_frames, self.useTrajectorySettings(),
dsm if self.useCustomSettings() else None, self.cooltowarmcolor_map
if self.isApplyTimeBasedGradient() else None)
def _deleteSelected(self):
"""
Delete selected frames from the list widget.
In case if display mode was active and selected frames were
in the workspace, we should also update workspace.
"""
self._deleteFrameItems(self.ui.frame_listw.selectedItems())
self._updateWorkspace()
def _deleteFrameItems(self, frame_items):
"""
Delete all frames specified in the frame_items list.
:type frame_items: list(FrameListItem)
:param frame_items: List of frame items to be deleted.
"""
frame_listw = self.ui.frame_listw
rows = sorted([frame_listw.row(item) for item in frame_items])
for row in reversed(rows):
frame_listw.takeItem(row)
self._updateFrameListAndDependentButtons()
def _deleteAll(self):
"""
Delete all frames from the list.
When all frames are deleted and display frames snapshot was active,
we should exit from display snapshot mode as well.
"""
self.ui.frame_listw.clear()
self._updateFrameListAndDependentButtons()
self._updateWorkspace()
def _getFramesSetByStep(self):
"""
Return frame set generated by step size for 1-total_frame range.
"""
step_size = self._getStep()
return set(frame for frame in range(1, self._player.total_frame +
1, step_size))
def _getFramesSetBySpecifiedFrames(self):
"""
Return frame set generated by the list or range.
"""
specified_frames_text = self._getSpecifiedFramesText()
frames_set = set()
for token in self._frame_range_validator.getTokens(
specified_frames_text):
if self._frame_range_validator.isRangeToken(token):
frame_range = self._frame_range_validator.getRange(token)
frames_set |= set(
frame_number
for frame_number in range(frame_range[0], frame_range[1] +
1))
else:
frames_set.add(int(token))
return frames_set
@property
def list_widget_item_count(self):
"""
:rtype: int
:return: Total frames listed in the frame list widget.
"""
return self.ui.frame_listw.count()
def _getCurrentFramesToDisplay(self):
"""
Get a list of frames which should be displayed in the workspace.
:rtype: list
:return: List of frame numbers.
"""
items_count = self.list_widget_item_count
frames = []
for frame in range(0, items_count):
frame_item = self.ui.frame_listw.item(frame)
if frame_item and frame_item.visible:
frames.append(frame_item.frame_number)
frames.sort()
return frames
def _getCurrentFramesSet(self):
"""
Scan through frame list widget and returns a set of all frames present
in the list widget.
:rtype: set(int)
:return: Set of frame number. If there is no frame, returns a empty
frame set.
"""
items_count = self.list_widget_item_count
frames_set = set()
for frame in range(0, items_count):
frame_item = self.ui.frame_listw.item(frame)
if frame_item:
frames_set.add(frame_item.frame_number)
return frames_set
def _addSnapshotFrames(self):
"""
Add frames in the snapshot widget list and update related UI buttons.
"""
old_frames_set = self._getCurrentFramesSet()
new_frames_set = set()
if self.ui.current_workspace_frame_radiob.isChecked():
new_frames_set.add(self._getCurrentFrame())
elif self.ui.stepwise_radio_button.isChecked():
new_frames_set |= self._getFramesSetByStep()
else:
new_frames_set |= self._getFramesSetBySpecifiedFrames()
new_frames_to_add = new_frames_set - old_frames_set
# Add all new frames in the list widget.
if new_frames_to_add:
for frame in new_frames_to_add:
self.ui.frame_listw.addItem(FrameListItem(frame))
self._updateFrameListAndDependentButtons()
[docs] def aboutToQuitMaestro(self):
"""
When Maestro is about to quit, this function gets called.
"""
if self.is_display_snapshot_active:
self.exitDisplaySnapshotMode()