Source code for schrodinger.ui.qt.standard_widgets.buttons
"""
Module containing classes for buttons styled for use in Schrodinger panels.
"""
import enum
from schrodinger.Qt import QtWidgets
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtGui
from schrodinger.Qt.QtWidgets import QSizePolicy
from schrodinger.ui.qt import style
from schrodinger.ui.qt.standard.colors import LightModeColors
[docs]def apply_style_sheet_to_button(button, style, margin_height=0, margin_width=3):
"""
Apply a Schrodinger color scheme CSS to the given QPushButton object.
:param button: Button to apply the styling to.
:type button: QtWidgets.QPushButton
:param style: Style to use
:type style: `StyledButton.Style`
:param margin_height: Margin height of the PushButton in pixels
:type margin_height: Union[float, int]
:param margin_width: Margin width of the PushButton in pixels
:type margin_width: Union[float, int]
"""
background_gradient = LightModeColors.STANDARD_BUTTON_BACKGROUND
pressed_bg_color = LightModeColors.STANDARD_PRESSED_BUTTON_BACKGROUND_COLOR
if style == StyledButton.Style.Highlighted:
background_gradient = LightModeColors.HIGHLIGHTED_BUTTON_BACKGROUND
pressed_bg_color = LightModeColors.HIGHLIGHTED_PRESSED_BUTTON_BACKGROUND_COLOR
text_color = LightModeColors.HIGHLIGHTED_BUTTON_COLOR
elif style == StyledButton.Style.HighlightedText:
text_color = LightModeColors.GOOD_TEXT
else:
if style != StyledButton.Style.Standard:
raise ValueError('Invalid style specified')
text_color = LightModeColors.STANDARD_BUTTON_COLOR
css = f'''
QPushButton {{
padding: -5px 20px; /* spacing WITHIN; make button shorter & wider */
margin: {margin_height}px {margin_width}px; /* spacing between buttons */
height: 28px;
color: {text_color};
background-color: {background_gradient};
border-radius: 2px;
border: 1px solid {LightModeColors.STANDARD_BUTTON_BORDER_ENABLED};
}}
QPushButton:!enabled {{
color: {LightModeColors.DISABLED_BUTTON_COLOR};
background-color: {LightModeColors.DISABLED_BUTTON_BACKGROUND};
border: 1px solid {LightModeColors.DISABLED_BUTTON_BORDER}
}}
QPushButton:pressed {{
color: {LightModeColors.PRESSED_BUTTON_COLOR};
background-color: {pressed_bg_color};
}}
'''
button.setStyleSheet(css)
[docs]class DoubleClickButtonMixin:
"""
Mixin that includes a double click signal for push button.
Include this mixin with a QPushButton subclass and
singleClicked and doubleClicked will work out of the box.
:ivar DOUBLE_CLICK_TIME: the amount of time to trigger double click (ms)
:vartype DOUBLE_CLICK_TIME: int
:ivar doubleClicked: a signal that the button was double clicked
:vartype doubleClicked: QtCore.pyqtSignal
:ivar singleClicked: a signal that the button was single clicked
:vartype singleClicked: QtCore.pyqtSignal
"""
doubleClicked = QtCore.pyqtSignal()
singleClicked = QtCore.pyqtSignal()
[docs] def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.click_timer = QtCore.QTimer()
self.click_timer.setSingleShot(True)
self.click_timer.timeout.connect(self.singleClicked)
super().clicked.connect(self._checkClickType)
@QtCore.pyqtSlot()
def _checkClickType(self):
double_click_time = QtWidgets.QApplication.instance(
).doubleClickInterval()
self.click_timer.start(double_click_time)
[docs] def mouseDoubleClickEvent(self, event: QtGui.QMouseEvent):
if event.button() == QtCore.Qt.LeftButton:
self.doubleClicked.emit()
self.click_timer.stop()
[docs]class StyledButton(QtWidgets.QPushButton):
"""
A QPushButton that uses custom CSS, and offers ability to be "highlighted"
with a green background (e.g. for Run buttons).
"""
[docs] class Style(enum.Enum):
# Standard button - gray background, black text
Standard = 'Standard' # gray background, black text
# Green background, white text - similar to "defualt" button, except
# that "Enter" key is not mapped to it.
Highlighted = 'Highlighted'
# Standard button with green text:
HighlightedText = 'HighlightedText'
[docs] def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._style = self.Style.Standard
self.updateStyleSheet()
[docs] def setStyle(self, style):
"""
Set the style of the button to one of available styles.
:param style: Style to use.
:type stle: `StyledButton.Style`
"""
self._style = style
self.updateStyleSheet()
[docs] def setHighlighted(self, highlighted):
"""
Whether to make the button background green, to make the button "jump
out" to the user as the next likely action to take. Similar to a
"default" button state (often used in dialog boxes), except that it
doesn't activate on pressing of the "Enter" key.
"""
self._style = self.Style.Highlighted if highlighted else self.Style.Standard
self.updateStyleSheet()
[docs]class SplitStyledButton(StyledButton):
"""
A StyledButton with no margin used for adjacent alignment.
Create a split button by placing multiple in an individual HBoxLayout with no spacing.
"""
[docs]class DoubleClickSplitStyledButton(DoubleClickButtonMixin, SplitStyledButton):
"""
Button with split styling and double click signal.
"""
[docs]class FlatButton(QtWidgets.QToolButton):
"""
A flat icon toolbutton. To use this, it's necessary to call setIconPath
with the appropriate path to get the icon to show up. If a different icon
is to be used for hover/pressed states, call setHoverIconPath. The size of
the icon can be specified using `setIconSize_` (there is already a Qt
setIconSize method on the class)
"""
[docs] def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._normal_path = ""
self._hover_path = ""
self._disabled_path = ""
self._pressed_path = ""
self._icon_width = 20
self._icon_height = 20
self.updateStyleSheet()
[docs] def setHoverIconPath(self, hover_path):
self._hover_path = hover_path
self.updateStyleSheet()
[docs] def setDisabledIconPath(self, disabled_path):
self._disabled_path = disabled_path
self.updateStyleSheet()
[docs] def setPressedIconPath(self, pressed_path):
self._pressed_path = pressed_path
self.updateStyleSheet()
[docs] def setIconSize_(self, width=None, height=None):
if width:
self._icon_width = width
if height:
self._icon_height = height
self.updateStyleSheet()
[docs] def updateStyleSheet(self):
ss = """
QToolButton {
width: """ + str(self._icon_width) + """px;
height: """ + str(self._icon_height) + """px;
border: 1px solid transparent;
background: none;
padding: 0px 0px 0px 0px;
}"""
if self._normal_path:
ss += """
QToolButton {
image: url(""" + self._normal_path + """);
}"""
if self._hover_path:
ss += """
QToolButton:hover,
QToolButton:pressed {
image: url(""" + self._hover_path + """);
}"""
if self._disabled_path:
ss += """
QToolButton:disabled {
image: url(""" + self._disabled_path + """);
}"""
if self._pressed_path:
ss += """
QToolButton:pressed {
image: url(""" + self._pressed_path + """);
}"""
self.setStyleSheet(ss)
# ==============================================================================
# PANELX BUTTONS
# ==============================================================================
"""
The PanelX design system includes 6 standard buttons. Classes are defined here
and should be styled through the parent panel by applying the PanelX stylesheet
to the panel.
"""
class _DropdownMixin:
"""
A temporary mixin to test out PanelX menu visuals. Eventually we can get rid
of this.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.dropdown_menu = QtWidgets.QMenu(parent=self)
self.setMenu(self.dropdown_menu)
self.dropdown_menu.addAction('foo')
self.dropdown_menu.addAction('bar')
self.dropdown_menu.addAction('baz')
class _PanelXPushButton(QtWidgets.QPushButton):
"""
Push button with fixed size policy for use in all PanelX buttons.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
[docs]class PrimaryPushButton(_PanelXPushButton):
"""
Subclass to apply primary button styling.
"""
pass
[docs]class SecondaryPushButton(_PanelXPushButton):
"""
Subclass to apply secondary button styling.
"""
pass
[docs]class TertiaryPushButton(_PanelXPushButton):
"""
Subclass to apply tertiary button styling.
"""
pass
[docs]class DropdownPushButton(_PanelXPushButton, _DropdownMixin):
"""
Push button with a link and configurable dropdown.
"""
pass
class _LeftSplitPushButton(PrimaryPushButton):
"""
PanelX button with no horizontal margin and rounded left corners. This the
primary button of a SplitPushButton.
"""
pass
class _RightSplitPushButton(PrimaryPushButton, _DropdownMixin):
"""
PanelX button with no horizontal margin and rounded right corners. This the
secondary button of a SplitPushButton.
"""
pass
[docs]class SplitPushButton(QtWidgets.QWidget):
"""
PanelX split push button. Contains both the left and right buttons
"""
INTERBUTTON_SPACING = 1
# TODO: implement button functionality once we get a spec (maybe using
# taskbar redesign spec?)
[docs] def __init__(self, text: str = '', *args, **kwargs):
super().__init__(*args, **kwargs)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
# Manually apply stylesheet since this is only a QWidget
# Does not inherit styleSheet otherwise
style.apply_panelx_style(self)
self._text = text
self.left_btn = _LeftSplitPushButton(self._text)
self.right_btn = _RightSplitPushButton('')
self._initLayOut()
def _initLayOut(self):
"""
Lay out all children. Not to be confused with `InitMixin.initLayOut()`
"""
self._buttons_layout = QtWidgets.QHBoxLayout()
self._buttons_layout.setContentsMargins(0, 0, 0, 0)
self._buttons_layout.setSpacing(self.INTERBUTTON_SPACING)
self._buttons_layout.addWidget(self.left_btn)
self._buttons_layout.addWidget(self.right_btn)
self.setLayout(self._buttons_layout)