136 lines
5.7 KiB
Python
136 lines
5.7 KiB
Python
"""
|
|
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')
|