Skip to content

Commit

Permalink
Merge pull request #22230 from ccordoba12/fix-run-debugger-shortcuts
Browse files Browse the repository at this point in the history
PR: Fix shortcuts for several Run and Debugger actions
  • Loading branch information
dalthviz authored Jul 10, 2024
2 parents 46660c3 + 4a3a5fd commit ad4827e
Show file tree
Hide file tree
Showing 30 changed files with 492 additions and 708 deletions.
66 changes: 0 additions & 66 deletions spyder/api/config/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@
from typing import Any, Union, Optional
import warnings

# Third-party imports
from qtpy.QtWidgets import QAction, QWidget

# Local imports
from spyder.config.gui import Shortcut
from spyder.config.manager import CONF
from spyder.config.types import ConfigurationKey
from spyder.config.user import NoDefault
Expand Down Expand Up @@ -204,68 +200,6 @@ def get_conf_default(self,
)
return CONF.get_default(section, option)

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
Key identifier under which the shortcut is stored.
context: Optional[str]
Name of the shortcut context.
plugin: Optional[str]
Name of the plugin where the shortcut is defined.
Returns
-------
shortcut: str
Key sequence of the shortcut.
Raises
------
configparser.NoOptionError
If the section does not exist in the configuration.
"""
context = self.CONF_SECTION if context is None else context
return CONF.get_shortcut(context, name, plugin_name)

def config_shortcut(
self, action: QAction, name: str, parent: QWidget,
context: Optional[str] = None) -> Shortcut:
"""
Create a Shortcut namedtuple for a widget.
The data contained in this tuple will be registered in our shortcuts
preferences page.
Parameters
----------
action: QAction
Action that will use the shortcut.
name: str
Key identifier under which the shortcut is stored.
parent: QWidget
Parent widget for the shortcut.
context: Optional[str]
Name of the context (plugin) where the shortcut was defined.
Returns
-------
shortcut: Shortcut
Namedtuple with the information of the shortcut as used for the
shortcuts preferences page.
"""
shortcut_context = self.CONF_SECTION if context is None else context
return CONF.config_shortcut(
action,
shortcut_context,
name,
parent
)

@property
def old_conf_version(self):
"""Get old Spyder configuration version."""
Expand Down
116 changes: 116 additions & 0 deletions spyder/api/shortcuts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)

"""
Helper utilities to get the fonts used in Spyder from our config system.
"""

# Standard library imports
from typing import Callable, Optional

# Third-party imports
from qtpy.QtCore import Qt
from qtpy.QtGui import QKeySequence
from qtpy.QtWidgets import QShortcut, QWidget

# Local imports
from spyder.config.manager import CONF


class SpyderShortcutsMixin:
"""Provide methods to get, set and register shortcuts."""

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
Key identifier under which the shortcut is stored.
context: Optional[str]
Name of the shortcut context.
plugin: Optional[str]
Name of the plugin where the shortcut is defined.
Returns
-------
shortcut: str
Key sequence of the shortcut.
Raises
------
configparser.NoOptionError
If the context does not exist in the configuration.
"""
context = self.CONF_SECTION if context is None else context
return CONF.get_shortcut(context, name, plugin_name)

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
Key identifier under which the shortcut is stored.
context: Optional[str]
Name of the shortcut context.
plugin: Optional[str]
Name of the plugin where the shortcut is defined.
Raises
------
configparser.NoOptionError
If the context 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_for_widget(
self,
name: str,
triggered: Callable,
widget: Optional[QWidget] = None,
context: Optional[str] = None,
):
"""
Register a shortcut for a widget that inherits this mixin.
Parameters
----------
name: str
Key identifier under which the shortcut is stored.
triggered: Callable
Callable (i.e. function or method) that will be triggered by the
shortcut.
widget: Optional[QWidget]
Widget to which register this shortcut. By default we register it
to the one that calls this method.
context: Optional[str]
Name of the context (plugin) where the shortcut is defined. By
default we use the widget's CONF_SECTION.
"""
context = self.CONF_SECTION if context is None else context
widget = self if widget is None else widget

keystr = self.get_shortcut(name, context)
qsc = QShortcut(QKeySequence(keystr), widget)
qsc.activated.connect(triggered)
qsc.setContext(Qt.WidgetWithChildrenShortcut)
12 changes: 9 additions & 3 deletions spyder/api/widgets/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
SpyderConfigurationObserver
)
from spyder.api.exceptions import SpyderAPIError
from spyder.api.shortcuts import SpyderShortcutsMixin
from spyder.api.widgets.menus import SpyderMenu
from spyder.api.widgets.toolbars import SpyderToolbar
from spyder.config.manager import CONF
Expand Down Expand Up @@ -650,9 +651,14 @@ def update_actions(self, options):
raise NotImplementedError('')


class SpyderWidgetMixin(SpyderActionMixin, SpyderConfigurationObserver,
SpyderMenuMixin, SpyderToolbarMixin,
SpyderToolButtonMixin):
class SpyderWidgetMixin(
SpyderActionMixin,
SpyderConfigurationObserver,
SpyderMenuMixin,
SpyderToolbarMixin,
SpyderToolButtonMixin,
SpyderShortcutsMixin,
):
"""
Basic functionality for all Spyder widgets and Qt items.
Expand Down
4 changes: 3 additions & 1 deletion spyder/app/mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
set_opengl_implementation)
from spyder.api.plugin_registration.registry import PLUGIN_REGISTRY
from spyder.api.config.mixins import SpyderConfigurationAccessor
from spyder.api.shortcuts import SpyderShortcutsMixin
from spyder.api.widgets.mixins import SpyderMainWindowMixin
from spyder.config.base import (_, DEV, get_conf_path, get_debug_level,
get_home_dir, is_conda_based_app,
Expand Down Expand Up @@ -125,7 +126,8 @@
class MainWindow(
QMainWindow,
SpyderMainWindowMixin,
SpyderConfigurationAccessor
SpyderConfigurationAccessor,
SpyderShortcutsMixin,
):
"""Spyder main window"""
CONF_SECTION = 'main'
Expand Down
24 changes: 13 additions & 11 deletions spyder/app/tests/test_mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -3268,34 +3268,36 @@ def test_preferences_empty_shortcut_regression(main_window, qtbot):
timeout=SHELL_TIMEOUT)

# Setup shortcuts (set run cell and advance shortcut to run selection)
base_run_cell_advance = CONF.get_shortcut(
'editor', 'run cell and advance') # Should be Shift+Return
base_run_selection = CONF.get_shortcut(
'editor', 'run selection and advance') # Should be F9
base_run_cell_advance = main_window.get_shortcut(
'run cell and advance', 'editor'
) # Should be Shift+Return
base_run_selection = main_window.get_shortcut(
'run selection and advance', 'editor'
) # Should be F9
assert base_run_cell_advance == 'Shift+Return'
assert base_run_selection == 'F9'

CONF.set_shortcut(
'editor', 'run cell and advance', '')
CONF.set_shortcut(
'editor', 'run selection and advance', base_run_cell_advance)
main_window.set_shortcut('', 'run cell and advance', 'editor')
main_window.set_shortcut(
base_run_cell_advance, 'run selection and advance', 'editor'
)
with qtbot.waitSignal(main_window.shortcuts.sig_shortcuts_updated):
main_window.shortcuts.apply_shortcuts()

# Check execution of shortcut
# Create new file
main_window.editor.new()
code_editor = main_window.editor.get_focus_widget()
code_editor.set_text(u'print(0)\n#%%\nprint(ññ)')
code_editor.set_text('print(0)\n#%%\nprint(ññ)')

with qtbot.waitSignal(shell.executed):
qtbot.keyClick(code_editor, Qt.Key_Return, modifier=Qt.ShiftModifier)
qtbot.waitUntil(lambda: u'print(0)' in shell._control.toPlainText())
assert u'ññ' not in shell._control.toPlainText()

# Reset shortcuts
CONF.set_shortcut('editor', 'run selection and advance', 'F9')
CONF.set_shortcut('editor', 'run cell and advance', 'Shift+Return')
main_window.set_shortcut('F9', 'run selection and advance', 'editor')
main_window.set_shortcut('Shift+Return', 'run cell and advance', 'editor')

# Wait for shortcut change to actually be applied
with qtbot.waitSignal(main_window.shortcuts.sig_shortcuts_updated):
Expand Down
26 changes: 1 addition & 25 deletions spyder/config/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,17 @@
Ctrl + Alt + Q, W, F, G, Y, X, C, V, B, N
"""

# Standard library imports
from collections import namedtuple

# Third party imports
from qtconsole.styles import dark_color
from qtpy import QT_VERSION
from qtpy.QtCore import Qt
from qtpy.QtGui import QFont, QFontDatabase, QKeySequence
from qtpy.QtWidgets import QShortcut
from qtpy.QtGui import QFont, QFontDatabase

# Local imports
from spyder.config.manager import CONF
from spyder.py3compat import to_text_string
from spyder.utils import syntaxhighlighters as sh


# To save metadata about widget shortcuts (needed to build our
# preferences page)
Shortcut = namedtuple('Shortcut', 'data')


def font_is_installed(font):
"""Check if font is installed"""
db = QFontDatabase() if QT_VERSION.startswith("5") else QFontDatabase
Expand Down Expand Up @@ -97,20 +87,6 @@ def set_font(font, section='appearance', option='font'):
FONT_CACHE[(section, option, font_size_delta)] = font


def _config_shortcut(action, context, name, keystr, parent):
"""
Create a Shortcut namedtuple for a widget.
The data contained in this tuple will be registered in our shortcuts
preferences page.
"""
qsc = QShortcut(QKeySequence(keystr), parent)
qsc.activated.connect(action)
qsc.setContext(Qt.WidgetWithChildrenShortcut)
sc = Shortcut(data=(qsc, context, name))
return sc


def get_color_scheme(name):
"""Get syntax color scheme"""
color_scheme = {}
Expand Down
Loading

0 comments on commit ad4827e

Please sign in to comment.