Source code for schrodinger.trajectory.trajectory_gui_dir.snapshot_panel

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()