Source code for schrodinger.application.bioluminate.actions.factory

"""
Contains Qt actions specific to the BioLuminate application.

Copyright Schrodinger, LLC. All rights reserved.

"""

#- Imports -------------------------------------------------------------------

import re

from schrodinger.Qt import QtGui
from schrodinger.Qt import QtWidgets
from schrodinger.Qt.QtCore import Qt

#- Globals -------------------------------------------------------------------

ACTION_NAME = 'action_%(name)s'
SEPARATOR_RE = re.compile(r'^separator_\d+')

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


[docs]def get_action_obj_name(name): """ Helper function to get the action's object name according to the syntax useed to create the name. The syntax is "action_<text>" where text is the text used when setting the action. """ return ACTION_NAME % {'name': name}
[docs]def replace_slot(owner, name, slot): """ Removes all slots from the action and replaces it with another slot. :param owner: The owner of the action :param name: The name used when creating the `SAction` :param slot: The new slot to connect to :type slot: callable :raise AttributeError: When no action is found with that name in the owner """ objname = get_action_obj_name(name) action = owner.findChild(SAction, objname) if not action: msg = 'No action named "%s" was found in %s"' % (objname, owner.__class__) raise AttributeError(msg) # Disconnect the old slots and connect to the new slot action.disconnectSlots() action.connectSlot(slot_callable=slot)
#- Classes -------------------------------------------------------------------
[docs]class SAction(QtGui.QAction): """ A QAction that enforces standards that are expected by some BioLuminate methods. The `setObjectName` method is used and the name conforms to the global `ACTION_NAME` variable in the `factory` module. """
[docs] def __init__(self, parent, name, text='', tooltip='', status_tip='', icon=None, slot=None, parent_slot=None, checkable=False, checked=False, visible=True, data=None, shortcut=None): """ Create a SAction object :param parent: The parent object of this action. This is used for all `parent_slot` calls. :param name: This will be used to name the object. The name will have ``action_`` prepended to it. :type name: str :param text: The text to use for the action menus and toolbars :type text: str :param tooltip: The action's tooltip :type tooltip: str :param status_tip: The string to show in the status bar when the action is hovered over. :param status_tip: The string to show in the status bar when the action is hovered over. :param icon: The icon path used to create a `QtGui.QIcon` :type icon: string :param slot: The function called when action is executed :type slot: callable :param parent_slot: The name of the parent function called when action is executed :type parent_slot: string :param checkable: Whether action has on/off state. :type checkable: bool :param checkable: Whether action is on or off. Only good when `checkable` is True. :type checkable: bool :param visible: Whether the action is shown or hidden :type visible: bool :param data: Data associated with action :type data: mixed :param shortcut: Shortcut to action :type shortcut: string """ self.parent = parent super(SAction, self).__init__(parent) # Setting the objecet name helps out in a few ways. # PyQT made the object name similar to class lookup # in a DOM, where you can find an object by name (and type) # by using findChild(). Multiple objects use findChildren(). self.setObjectName(ACTION_NAME % {'name': name}) if icon: self.setIcon(QtGui.QIcon(icon)) # The next three paramters are viewable by customers # so we translate the strings self.setText(self.tr(text)) self.setToolTip(self.tr(tooltip)) self.setStatusTip(self.tr(status_tip)) # Turn this into a toggle-able action if requested if checkable: self.setCheckable(True) if data is not None: self.setData(data) if shortcut: self.setShortcut(shortcut) self.setVisible(visible) # Connect the action to a slot if slot or parent_slot: self.connectSlot(slot, parent_slot) # Set the check state after the action has been connected to # its slot so the slot is called if checkable and checked: self.setChecked(checked)
[docs] def connectSlot(self, slot_callable=None, slot_name=None): """ Connects action to a slot by either supplying a callable or a method name that belongs to the action's parent widget. The `slot_callable` will override `slot_name` if both are passed in. :param slot_callable: A function passed in to connect to the action :type slot_callable: callable :param slot_name: A method name found in the actions parent :type slot_name: string """ if slot_callable is None: if hasattr(self.parent, slot_name): slot_callable = getattr(self.parent, slot_name) if slot_callable: if self.isCheckable(): self.toggled.connect(slot_callable) else: self.triggered.connect(slot_callable)
[docs] def disconnectSlots(self): """ Disconnects slots connected to this action. """ if self.isCheckable(): self.toggled.disconnect() else: self.triggered.disconnect()
[docs]class Factory: """ A class dedicated to the creation, deletion and modification of groups of actions. """
[docs] def __init__(self, parent): self.parent = parent self.actions = []
[docs] def connectAction(self, action, slot): """ Connects an action to a slot """ if action.isCheckable(): action.toggled.connect(slot) else: action.triggered.connect(slot)
[docs] def getAction(self, action_name): """ Gets an action from the action name """ obj_name = ACTION_NAME % {'name': action_name} action = self.parent.findChild(SAction, obj_name) return action
[docs] def clearActions(self): """ Clears all actions in the factory """ self.actions = []
[docs] def removeAction(self, action_name): """ Removes an action from the stored actions based on action name """ self.actions = [ a for a in self.actions if a.objectName() != action_name ]
[docs] def createActions(self, action_params, action_order=None, parent=None): """ Creates actions. :param action_params: Parameters used to create an action. :type action_params: dict :param action_order: Order of actions (must be keys in `action_params` :type action_order: list of strings :param parent: Parent of actions. Default: `self.parent` :type parent: object :see: `SAction.__init__` """ parent = parent or self.parent action_order = action_order or action_params.get('default_order', []) if not action_order: raise Exception('A default_order is needed') actions = [] for name in action_order: if SEPARATOR_RE.search(name): action = QtGui.QAction(parent) action.setSeparator(True) else: params = action_params.get(name) if params: action = SAction(parent, name, **params) actions.append(action) return actions
[docs] def addActions(self, action_params, action_order=None, parent=None): """ Add actions to the current action list """ actions = self.createActions(action_params, action_order=action_order, parent=parent) self.actions.extend(actions)
[docs] def setActions(self, action_params, action_order=None, parent=None): """ Replace current acction with new actions """ actions = self.createActions(action_params, action_order=action_order, parent=parent) self.actions = actions
[docs] def getMenu(self): """ Return `self.actions` list as a `QtWidgets.QMenu` """ menu = QtWidgets.QMenu(self.parent) menu.addActions(self.actions) # On Darwin newly created menus may appear when a panel # is first time displayed. To prevent this from happening # we need to explicitly hide the menu (Ev:128367) menu.hide() return menu
[docs] def getMenuBar(self): """ Return `self.actions` list as a `QtWidgets.QMenuBar` """ menubar = QtWidgets.QMenuBar(self.parent) menubar.addActions(self.actions) return menubar
[docs] def getToolBar(self, title=None, movable=False): """ Return `self.actions` list as a `QtWidgets.QToolBar` :param title: Title to set to the toolbar """ toolbar = QtWidgets.QToolBar(self.parent) toolbar.addActions(self.actions) toolbar.setMovable(movable) if title: toolbar.setWindowTitle(title) return toolbar
[docs] def addContext(self, parent=None): """ Add a context menu to parent. :param parent: Parent of actions. Default: `self.parent` :type parent: object """ parent = parent or self.parent parent.setContextMenuPolicy(Qt.ActionsContextMenu) parent.addActions(self.actions)
[docs] def addSubMenu(self, action_params, action_order=None, parent=None, add_separator=False, text=None, icon=None): """ Adds a submenu to an action and appends the action to `self.actions`. """ parent = parent or self.parent if add_separator: sep = QtGui.QAction(parent) sep.setSeparator(True) self.actions.append(sep) menu_action = QtGui.QAction(parent) if icon is not None: menu_action.setIcon(icon) if text is not None: menu_action.setText(text) submenu = QtWidgets.QMenu() submenu_actions = self.createActions(action_params, action_order=action_order, parent=parent) submenu.addActions(submenu_actions) menu_action.setMenu(submenu) self.actions.append(menu_action)