""" This module contains the Interactive python console, for running python programs. """ from pyqode.qt import QtCore, QtGui, QtWidgets from pyqode.core.widgets import InteractiveConsole class PyInteractiveConsole(InteractiveConsole): """ Extends the InteractiveConcole to highlight python traceback. If the user press on a filename in a traceback, the signal open_file_requested is emitted with the file path to open and the line where the user want to go. """ #: Signal emitted when the user pressed on a traceback file location. #: Client code should open the requested file in the editor. open_file_requested = QtCore.Signal(str, int) class UserData(QtGui.QTextBlockUserData): def __init__(self, filename, line, start, end): super(PyInteractiveConsole.UserData, self).__init__() self.filename = filename self.line = line self.start_pos_in_block = start self.end_pos_in_block = end def __init__(self, parent=None): super(PyInteractiveConsole, self).__init__(parent) self.set_writer(self._write) self.setMouseTracking(True) self.PROG = QtCore.QRegExp( r'\s*File "[a-zA-Z\/_\d:\\\.]*((.\.[a-zA-Z\/_\d:\\]*")|(")), ' r'line [0-9]*.*') self.FILENAME_PROG = QtCore.QRegExp(r'"[a-zA-Z\/_\.\d:\\]*"') self.LINE_PROG = QtCore.QRegExp(r'line [0-9]*') self.setLineWrapMode(self.NoWrap) self._module_color = QtGui.QColor('blue') def start_process(self, process, args=None, cwd=None, env=None): if env is None: env = {} if 'PYTHONUNBUFFERED' not in env: env['PYTHONUNBUFFERED'] = '1' super(PyInteractiveConsole, self).start_process( process, args, cwd, env) def _write(self, text_edit, text, color): def write(text_edit, text, color): text_edit.moveCursor(QtGui.QTextCursor.End) fmt = QtGui.QTextCharFormat() fmt.setUnderlineStyle(QtGui.QTextCharFormat.NoUnderline) fmt.setForeground(QtGui.QBrush(color)) text_edit.setCurrentCharFormat(fmt) text_edit.insertPlainText(text) text_edit.moveCursor(QtGui.QTextCursor.End) def write_with_underline(text_edit, text, color, line, start, end): text_edit.moveCursor(QtGui.QTextCursor.End) text_edit.setTextColor(color) fmt = QtGui.QTextCharFormat() fmt.setUnderlineColor(color) fmt.setUnderlineStyle(QtGui.QTextCharFormat.SingleUnderline) fmt.setForeground(QtGui.QBrush(color)) text_edit.setCurrentCharFormat(fmt) text_edit.insertPlainText(text) text_edit.moveCursor(QtGui.QTextCursor.End) block = self.document().lastBlock() data = self.UserData(text, line, start, end) block.setUserData(data) text = text.replace('\n', '{@}\n') text = text.replace('\r', '') for i, line in enumerate(text.split('{@}')): # check if File and highlight it in blue, also store it if self.PROG.indexIn(line) != -1: # get line number self.LINE_PROG.indexIn(line) start = self.LINE_PROG.pos(0) end = start + len(self.LINE_PROG.cap(0)) l = int(line[start:end].replace('line ', '')) - 1 self.FILENAME_PROG.indexIn(line) start = self.FILENAME_PROG.pos(0) end = start + len(self.FILENAME_PROG.cap(0)) write(self, line[:start + 1], color) write_with_underline(self, line[start + 1:end - 1], self._module_color, l, start, end) write(self, line[end - 1:], color) else: write(text_edit, line, color) def mouseMoveEvent(self, e): """ Extends mouseMoveEvent to display a pointing hand cursor when the mouse cursor is over a file location """ super(PyInteractiveConsole, self).mouseMoveEvent(e) cursor = self.cursorForPosition(e.pos()) assert isinstance(cursor, QtGui.QTextCursor) p = cursor.positionInBlock() usd = cursor.block().userData() if usd and usd.start_pos_in_block <= p <= usd.end_pos_in_block: if QtWidgets.QApplication.overrideCursor() is None: QtWidgets.QApplication.setOverrideCursor( QtGui.QCursor(QtCore.Qt.PointingHandCursor)) else: if QtWidgets.QApplication.overrideCursor() is not None: QtWidgets.QApplication.restoreOverrideCursor() def mousePressEvent(self, e): """ Emits open_file_requested if the press event occured over a file location string. """ super(PyInteractiveConsole, self).mousePressEvent(e) cursor = self.cursorForPosition(e.pos()) p = cursor.positionInBlock() usd = cursor.block().userData() if usd and usd.start_pos_in_block <= p <= usd.end_pos_in_block: if e.button() == QtCore.Qt.LeftButton: self.open_file_requested.emit(usd.filename, usd.line) def leaveEvent(self, e): super(PyInteractiveConsole, self).leaveEvent(e) if QtWidgets.QApplication.overrideCursor() is not None: QtWidgets.QApplication.restoreOverrideCursor() def apply_color_scheme(self, color_scheme): super(PyInteractiveConsole, self).apply_color_scheme(color_scheme) if color_scheme.background.lightness() < 128: self._module_color = QtGui.QColor('#0681e0') else: self._module_color = QtGui.QColor('blue')