# -*- coding: utf-8 -*- # # Copyright © Spyder Project Contributors # Licensed under the terms of the MIT License # (see spyder/__init__.py for details) """Control widgets used by ShellWidget""" # Third-party imports from qtpy.QtCore import Qt, QUrl, Signal from qtpy.QtGui import QColor, QDesktopServices, QTextFrameFormat from qtpy.QtWidgets import QApplication, QTextEdit # Local imports from spyder.utils.palette import QStylePalette from spyder.utils.qthelpers import restore_keyevent from spyder.widgets.calltip import CallTipWidget from spyder.widgets.mixins import (BaseEditMixin, GetHelpMixin, TracebackLinksMixin) class ControlWidget(TracebackLinksMixin, GetHelpMixin, QTextEdit, BaseEditMixin): """ Subclass of QTextEdit with features from Spyder's mixins to use as the control widget for IPython widgets """ QT_CLASS = QTextEdit sig_visibility_changed = Signal(bool) sig_go_to_error_requested = Signal(str) sig_focus_changed = Signal() sig_help_requested = Signal(dict) """ This signal is emitted to request help on a given object's `name`. help_data: dict Example `{'name': str, 'ignore_unknown': bool}`. """ def __init__(self, parent=None): QTextEdit.__init__(self, parent) BaseEditMixin.__init__(self) TracebackLinksMixin.__init__(self) GetHelpMixin.__init__(self) self.calltip_widget = CallTipWidget(self, hide_timer_on=False) self.found_results = [] # To not use Spyder calltips obtained through the monitor self.calltips = False # To detect anchors and make them clickable self.anchor = None # ---- Public methods ---------------------------------------------------- def insert_horizontal_ruler(self): """ Insert a horizontal ruler with the appropriate color according to our theme in the current cursor position. We have to do this because html hr elements can't be stylized in QTextEdit. Taken from https://stackoverflow.com/a/50016969/438386 """ ruler = QTextFrameFormat() ruler.setHeight(1) ruler.setWidth(10000) ruler.setBackground(QColor(QStylePalette.COLOR_TEXT_1)) cursor = self.textCursor() cursor.insertFrame(ruler) # ---- Private methods --------------------------------------------------- def _key_paren_left(self, text): """ Action for '(' """ self.current_prompt_pos = self.parentWidget()._prompt_pos if self.get_current_line_to_cursor(): last_obj = self.get_last_obj() if last_obj and not last_obj.isdigit(): self.show_object_info(last_obj) self.insert_text(text) # ---- Qt methods -------------------------------------------------------- def showEvent(self, event): """Reimplement Qt Method""" self.sig_visibility_changed.emit(True) def keyPressEvent(self, event): """Reimplement Qt Method - Basic keypress event handler""" event, text, key, ctrl, shift = restore_keyevent(event) if (key == Qt.Key_ParenLeft and not self.has_selected_text() and self.help_enabled and not self.parent()._reading): self._key_paren_left(text) else: # Let the parent widget handle the key press event QTextEdit.keyPressEvent(self, event) def focusInEvent(self, event): """Reimplement Qt method to send focus change notification""" self.sig_focus_changed.emit() return super(ControlWidget, self).focusInEvent(event) def focusOutEvent(self, event): """Reimplement Qt method to send focus change notification""" self.sig_focus_changed.emit() return super(ControlWidget, self).focusOutEvent(event) def mouseMoveEvent(self, event): """Detect anchors and change cursor shape accordingly.""" self.anchor = self.anchorAt(event.pos()) if self.anchor: QApplication.setOverrideCursor(Qt.PointingHandCursor) else: QApplication.restoreOverrideCursor() super(ControlWidget, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): """Ooen anchors when clicked.""" if self.anchor: QDesktopServices.openUrl(QUrl(self.anchor)) QApplication.setOverrideCursor(Qt.ArrowCursor) self.anchor = None else: super(ControlWidget, self).mouseReleaseEvent(event) class PageControlWidget(QTextEdit, BaseEditMixin): """ Subclass of QTextEdit with features from Spyder's mixins.BaseEditMixin to use as the paging widget for IPython widgets """ QT_CLASS = QTextEdit sig_visibility_changed = Signal(bool) sig_show_find_widget_requested = Signal() sig_focus_changed = Signal() def __init__(self, parent=None): QTextEdit.__init__(self, parent) BaseEditMixin.__init__(self) self.found_results = [] def showEvent(self, event): """Reimplement Qt Method""" self.sig_visibility_changed.emit(True) def keyPressEvent(self, event): """Reimplement Qt Method - Basic keypress event handler""" event, text, key, ctrl, shift = restore_keyevent(event) if key == Qt.Key_Slash and self.isVisible(): self.sig_show_find_widget_requested.emit() else: # Let the parent widget handle the key press event QTextEdit.keyPressEvent(self, event) def focusInEvent(self, event): """Reimplement Qt method to send focus change notification""" self.sig_focus_changed.emit() return super(PageControlWidget, self).focusInEvent(event) def focusOutEvent(self, event): """Reimplement Qt method to send focus change notification""" self.sig_focus_changed.emit() return super(PageControlWidget, self).focusOutEvent(event)