Source code for spyder.api.widgets.status

# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
#
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)

"""Status bar widgets."""

# Third party imports
import qstylizer.parser
from qtpy import PYSIDE2
from qtpy.QtCore import Qt, QSize, QTimer, Signal
from qtpy.QtGui import QIcon
from qtpy.QtWidgets import QHBoxLayout, QLabel, QWidget

# Local imports
from spyder.api.exceptions import SpyderAPIError
from spyder.api.widgets.mixins import SpyderWidgetMixin
from spyder.utils.palette import SpyderPalette
from spyder.utils.qthelpers import create_waitspinner
from spyder.utils.stylesheet import MAC


[docs] class StatusBarWidget(QWidget, SpyderWidgetMixin): """ Base class for status bar widgets. These widgets consist by default of an icon, a label and a spinner, which are organized from left to right on that order. You can also add any other QWidget to this layout by setting the CUSTOM_WIDGET_CLASS class attribute. It'll be put between the label and the spinner. """ ID = None """ Unique string widget identifier. """ CUSTOM_WIDGET_CLASS = None """ Custom widget class to add to the default layout. """ INTERACT_ON_CLICK = False """ Whether the user can interact with widget when clicked (e.g. to show a menu) """ sig_clicked = Signal() """ This signal is emmitted when the widget is clicked. """ def __init__(self, parent=None, show_icon=True, show_label=True, show_spinner=False): """ Base class for status bar widgets. These are composed of the following widgets, which are arranged in a QHBoxLayout from left to right: * Icon * Label * Custom QWidget * Spinner Parameters ---------- show_icon: bool Show an icon in the widget. show_label: bool Show a label in the widget. show_spinner: bool Show a spinner. Notes ----- 1. To use an icon, you need to redefine the ``get_icon`` method. 2. To use a label, you need to call ``set_value``. """ if not PYSIDE2: super().__init__(parent, class_parent=parent) else: QWidget.__init__(self, parent) SpyderWidgetMixin.__init__(self, class_parent=parent) self._parent = parent self.show_icon = show_icon self.show_label = show_label self.show_spinner = show_spinner self.value = None self.label_icon = None self.label_value = None self.spinner = None self.custom_widget = None # In case the widget has an associated menu self.menu = None self._set_layout() self._css = self._generate_stylesheet() self.setStyleSheet(self._css.toString()) # ---- Private API # ------------------------------------------------------------------------- def _set_layout(self): """Set layout for default widgets.""" # Icon if self.show_icon: self._icon = self.get_icon() self._pixmap = None self._icon_size = QSize(16, 16) # Should this be adjustable? self.label_icon = QLabel(self) self.set_icon() # Label if self.show_label: self.label_value = QLabel(self) self.set_value('') self.label_value.setAlignment(Qt.AlignRight | Qt.AlignVCenter) # Custom widget if self.CUSTOM_WIDGET_CLASS: if not issubclass(self.CUSTOM_WIDGET_CLASS, QWidget): raise SpyderAPIError( 'Any custom status widget must subclass QWidget!' ) self.custom_widget = self.CUSTOM_WIDGET_CLASS(self._parent) # Spinner if self.show_spinner: self.spinner = create_waitspinner(size=14, parent=self) self.spinner.hide() # Layout setup layout = QHBoxLayout(self) layout.setSpacing(0) # Reduce space between icon and label if self.show_icon: layout.addWidget(self.label_icon) if self.show_label: layout.addWidget(self.label_value) if self.custom_widget: layout.addWidget(self.custom_widget) if self.show_spinner: layout.addWidget(self.spinner) layout.addSpacing(0) layout.setContentsMargins(0, 0, 0, 0) layout.setAlignment(Qt.AlignVCenter) # Setup self.update_tooltip() def _generate_stylesheet(self): """Generate the widget's stylesheet.""" # Remove opacity that comes from QDarkstyle. # This work around is necessary because qstylizer doesn't have support # for the opacity property. initial_css = "QToolTip {opacity: 255;}" css = qstylizer.parser.parse(initial_css) # Make style match the one set for other tooltips in the app css.QToolTip.setValues( color=SpyderPalette.COLOR_TEXT_1, backgroundColor=SpyderPalette.COLOR_ACCENT_2, border="none", padding="1px 2px", ) return css # ---- Public API # -------------------------------------------------------------------------
[docs] def get_icon(self): """Get the widget's icon.""" return None
[docs] def set_icon(self): """Set the icon for the status bar widget.""" if self.label_icon: icon = self._icon self.label_icon.setVisible(icon is not None) if icon is not None and isinstance(icon, QIcon): self._pixmap = icon.pixmap(self._icon_size) self.label_icon.setPixmap(self._pixmap)
[docs] def set_value(self, value): """Set formatted text value.""" if self.label_value: self.value = value self.label_value.setText(value)
[docs] def get_tooltip(self): """Get the widget's tooltip text.""" return ''
[docs] def update_tooltip(self): """Update tooltip for widget.""" tooltip = self.get_tooltip() if tooltip: if self.label_value: self.label_value.setToolTip(tooltip) if self.label_icon: self.label_icon.setToolTip(tooltip) self.setToolTip(tooltip)
# ---- Qt methods # -------------------------------------------------------------------------
[docs] def mousePressEvent(self, event): """Change background color when the widget is clicked.""" if self.INTERACT_ON_CLICK: self._css.QWidget.setValues( backgroundColor=SpyderPalette.COLOR_BACKGROUND_6 ) self.setStyleSheet(self._css.toString()) super().mousePressEvent(event)
[docs] def mouseReleaseEvent(self, event): """ Change background color and emit signal to inform the widget was clicked. """ super().mouseReleaseEvent(event) if self.INTERACT_ON_CLICK: self._css.QWidget.setValues( # Mac doesn't correctly restore the background color after # clicking on a widget that shows a menu backgroundColor=SpyderPalette.COLOR_BACKGROUND_4 if MAC and self.menu else SpyderPalette.COLOR_BACKGROUND_5 ) self.setStyleSheet(self._css.toString()) self.sig_clicked.emit()
[docs] def enterEvent(self, event): """Change background color and cursor shape on hover.""" if self.INTERACT_ON_CLICK: self._css.QWidget.setValues( backgroundColor=SpyderPalette.COLOR_BACKGROUND_5 ) self.setStyleSheet(self._css.toString()) self.setCursor(Qt.PointingHandCursor) self.update_tooltip() super().enterEvent(event)
[docs] def leaveEvent(self, event): """Restore background color when not hovering.""" if self.INTERACT_ON_CLICK: self._css.QWidget.setValues( backgroundColor=SpyderPalette.COLOR_BACKGROUND_4 ) self.setStyleSheet(self._css.toString()) super().leaveEvent(event)
[docs] class BaseTimerStatus(StatusBarWidget): """ Base class for status bar widgets that update based on timers. """ def __init__(self, parent=None): """Base class for status bar widgets that update based on timers.""" self.timer = None # Needs to come before parent call super().__init__(parent) self._interval = 2000 # Widget setup fm = self.label_value.fontMetrics() self.label_value.setMinimumWidth(fm.width('000%')) # Setup self.timer = QTimer(self) self.timer.timeout.connect(self.update_status) self.timer.start(self._interval) # ---- Qt methods # -------------------------------------------------------------------------
[docs] def closeEvent(self, event): self.timer.stop() super().closeEvent(event)
[docs] def setVisible(self, value): """Stop timer if widget is not visible.""" if self.timer is not None: if value: self.timer.start(self._interval) else: self.timer.stop() super().setVisible(value)
# ---- Public API # -------------------------------------------------------------------------
[docs] def update_status(self): """Update status label widget, if widget is visible.""" if self.isVisible(): self.label_value.setText(self.get_value())
[docs] def set_interval(self, interval): """Set timer interval (ms).""" self._interval = interval if self.timer is not None: self.timer.setInterval(interval)
[docs] def get_value(self): """Return formatted text value.""" raise NotImplementedError