# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)
"""
Helper classes to get and set shortcuts in Spyder.
"""
# Standard library imports
import functools
from typing import Callable, Dict, Optional
# Third-party imports
from qtpy.QtCore import Qt
from qtpy.QtGui import QKeySequence
from qtpy.QtWidgets import QShortcut, QWidget
# Local imports
from spyder.api.config.mixins import SpyderConfigurationObserver
from spyder.config.manager import CONF
from spyder.plugins.shortcuts.utils import (
ShortcutData,
SHORTCUTS_FOR_WIDGETS_DATA,
)
[docs]
class SpyderShortcutsMixin(SpyderConfigurationObserver):
"""Provide methods to get, set and register shortcuts for widgets."""
def __init__(self):
super().__init__()
# This is used to keep track of the widget shortcuts
self._shortcuts: Dict[(str, str), QShortcut] = {}
[docs]
def get_shortcut(
self,
name: str,
context: Optional[str] = None,
plugin_name: Optional[str] = None,
) -> str:
"""
Get a shortcut sequence stored under the given name and context.
Parameters
----------
name: str
The shortcut name (e.g. "run cell").
context: str, optional
Name of the shortcut context, e.g. "editor" for shortcuts that have
effect when the Editor is focused or "_" for global shortcuts. If
not set, the widget's CONF_SECTION will be used as context.
plugin_name: str, optional
Name of the plugin where the shortcut is defined. This is necessary
for third-party plugins that have shortcuts with a context
different from the plugin name.
Returns
-------
shortcut: str
Key sequence of the shortcut.
Raises
------
configparser.NoOptionError
If the shortcut does not exist in the configuration.
"""
context = self.CONF_SECTION if context is None else context
return CONF.get_shortcut(context, name, plugin_name)
[docs]
def set_shortcut(
self,
shortcut: str,
name: str,
context: Optional[str] = None,
plugin_name: Optional[str] = None,
):
"""
Set a shortcut sequence with a given name and context.
Parameters
----------
shortcut: str
Key sequence of the shortcut.
name: str
The shortcut name (e.g. "run cell").
context: str, optional
Name of the shortcut context, e.g. "editor" for shortcuts that have
effect when the Editor is focused or "_" for global shortcuts. If
not set, the widget's CONF_SECTION will be used as context.
plugin_name: str, optional
Name of the plugin where the shortcut is defined. This is necessary
for third-party plugins that have shortcuts with a context
different from the plugin name.
Raises
------
configparser.NoOptionError
If the shortcut does not exist in the configuration.
"""
context = self.CONF_SECTION if context is None else context
return CONF.set_shortcut(context, name, shortcut, plugin_name)
def _register_shortcut(
self,
keystr: str,
name: str,
triggered: Callable,
context: str,
widget: QWidget,
plugin_name: Optional[str]
):
"""
Auxiliary function to register a shortcut for a widget.
Parameters
----------
keystr: str
Key string for the shortcut (e.g. "Ctrl+Enter").
name: str
The shortcut name (e.g. "run cell").
triggered: Callable
Callable (i.e. function or method) that will be triggered by the
shortcut.
widget: QWidget, optional
Widget to which this shortcut will be registered. If not set, the
widget that calls this method will be used.
context: str, optional
Name of the shortcut context, e.g. "editor" for shortcuts that have
effect when the Editor is focused or "_" for global shortcuts.
plugin_name: str, optional
Name of the plugin where the shortcut is defined. This is necessary
for third-party plugins that have shortcuts with a context
different from the plugin name.
"""
# Disable current shortcut, if available
current_shortcut = self._shortcuts.get((context, name, plugin_name))
if current_shortcut:
# Don't do the rest if we're trying to register the same shortcut
# again. This happens at startup because shortcuts are registered
# on widget creation and then the observer attached to the shortcut
# tries to do it again after CONF.notify_all_observers() is called.
if current_shortcut.key().toString() == keystr:
return
# Disable current shortcut to create a new one below
current_shortcut.setEnabled(False)
current_shortcut.deleteLater()
self._shortcuts.pop((context, name, plugin_name))
# Create a new shortcut
new_shortcut = QShortcut(QKeySequence(keystr), widget)
new_shortcut.activated.connect(triggered)
new_shortcut.setContext(Qt.WidgetWithChildrenShortcut)
# Save shortcut
self._shortcuts[(context, name, plugin_name)] = new_shortcut