15 |
15 |
- supports expressions that span through more lines
|
16 |
16 |
- has command history, accessible using up/down keys
|
17 |
17 |
- supports pasting of commands
|
|
18 |
- python syntax highlighing (portions of code from http://diotavelli.net/PyQtWiki/Python%20syntax%20highlighting)
|
18 |
19 |
|
19 |
20 |
TODO:
|
20 |
21 |
- configuration - init commands, font, ...
|
21 |
|
- python code highlighting
|
22 |
22 |
|
23 |
23 |
"""
|
24 |
24 |
|
... | ... | |
29 |
29 |
import traceback
|
30 |
30 |
import code
|
31 |
31 |
|
|
32 |
EDIT_LINE, ERROR, OUTPUT, INIT = range(4)
|
32 |
33 |
|
33 |
34 |
_init_commands = ["from qgis.core import *", "import qgis.utils"]
|
34 |
35 |
|
35 |
36 |
_console = None
|
36 |
37 |
|
|
38 |
def format(color, style = ''):
|
|
39 |
"""Return a QTextCharFormat with the given attributes.
|
|
40 |
"""
|
|
41 |
_color = QColor()
|
|
42 |
_color.setNamedColor(color)
|
|
43 |
|
|
44 |
_format = QTextCharFormat()
|
|
45 |
_format.setForeground(_color)
|
|
46 |
if 'bold' in style:
|
|
47 |
_format.setFontWeight(QFont.Bold)
|
|
48 |
if 'italic' in style:
|
|
49 |
_format.setFontItalic(True)
|
|
50 |
|
|
51 |
return _format
|
|
52 |
|
|
53 |
# Syntax styles that can be shared by all languages
|
|
54 |
STYLES = {
|
|
55 |
'keyword': format('blue'),
|
|
56 |
'operator': format('red'),
|
|
57 |
'brace': format('darkGray'),
|
|
58 |
'defclass': format('black', 'bold'),
|
|
59 |
'string': format('magenta'),
|
|
60 |
'string2': format('darkMagenta'),
|
|
61 |
'comment': format('darkGreen', 'italic'),
|
|
62 |
'self': format('black', 'italic'),
|
|
63 |
'numbers': format('brown'),
|
|
64 |
}
|
|
65 |
|
37 |
66 |
def show_console():
|
38 |
|
""" called from QGIS to open the console """
|
39 |
|
global _console
|
40 |
|
if _console is None:
|
41 |
|
_console = PythonConsole(iface.mainWindow())
|
42 |
|
_console.show() # force show even if it was restored as hidden
|
43 |
|
else:
|
44 |
|
_console.setVisible(not _console.isVisible())
|
45 |
|
# set focus to the edit box so the user can start typing
|
46 |
|
if _console.isVisible():
|
47 |
|
_console.activateWindow()
|
48 |
|
_console.edit.setFocus()
|
|
67 |
""" called from QGIS to open the console """
|
|
68 |
global _console
|
|
69 |
if _console is None:
|
|
70 |
_console = PythonConsole(iface.mainWindow())
|
|
71 |
_console.show() # force show even if it was restored as hidden
|
|
72 |
else:
|
|
73 |
_console.setVisible(not _console.isVisible())
|
|
74 |
# set focus to the edit box so the user can start typing
|
|
75 |
if _console.isVisible():
|
|
76 |
_console.activateWindow()
|
|
77 |
_console.edit.setFocus()
|
49 |
78 |
|
50 |
|
|
|
79 |
|
51 |
80 |
_old_stdout = sys.stdout
|
52 |
81 |
_console_output = None
|
53 |
82 |
|
54 |
83 |
|
55 |
84 |
def clearConsole():
|
56 |
|
global _console
|
57 |
|
if _console is None:
|
58 |
|
return
|
59 |
|
_console.edit.clearConsole()
|
|
85 |
global _console
|
|
86 |
if _console is None:
|
|
87 |
return
|
|
88 |
_console.edit.clearConsole()
|
60 |
89 |
|
61 |
|
|
62 |
90 |
# hook for python console so all output will be redirected
|
63 |
91 |
# and then shown in console
|
64 |
92 |
def console_displayhook(obj):
|
65 |
|
global _console_output
|
66 |
|
_console_output = obj
|
|
93 |
global _console_output
|
|
94 |
_console_output = obj
|
67 |
95 |
|
68 |
96 |
class QgisOutputCatcher:
|
69 |
|
def __init__(self):
|
70 |
|
self.data = ''
|
71 |
|
def write(self, stuff):
|
72 |
|
self.data += stuff
|
73 |
|
def get_and_clean_data(self):
|
74 |
|
tmp = self.data
|
75 |
|
self.data = ''
|
76 |
|
return tmp
|
77 |
|
def flush(self):
|
78 |
|
pass
|
|
97 |
def __init__(self):
|
|
98 |
self.data = ''
|
|
99 |
def write(self, stuff):
|
|
100 |
self.data += stuff
|
|
101 |
def get_and_clean_data(self):
|
|
102 |
tmp = self.data
|
|
103 |
self.data = ''
|
|
104 |
return tmp
|
|
105 |
def flush(self):
|
|
106 |
pass
|
79 |
107 |
|
80 |
108 |
sys.stdout = QgisOutputCatcher()
|
81 |
109 |
|
82 |
110 |
class PythonConsole(QDockWidget):
|
83 |
|
def __init__(self, parent=None):
|
84 |
|
QDockWidget.__init__(self, parent)
|
85 |
|
self.setObjectName("Python Console")
|
86 |
|
self.setAllowedAreas(Qt.BottomDockWidgetArea)
|
87 |
|
self.widget = QWidget()
|
88 |
|
self.l = QVBoxLayout(self.widget)
|
89 |
|
self.edit = PythonEdit()
|
90 |
|
self.l.addWidget(self.edit)
|
91 |
|
self.setWidget(self.widget)
|
92 |
|
self.setWindowTitle(QCoreApplication.translate("PythonConsole", "Python Console"))
|
93 |
|
# try to restore position from stored main window state
|
94 |
|
if not iface.mainWindow().restoreDockWidget(self):
|
95 |
|
iface.mainWindow().addDockWidget(Qt.BottomDockWidgetArea, self)
|
|
111 |
def __init__(self, parent = None):
|
|
112 |
QDockWidget.__init__(self, parent)
|
|
113 |
self.setObjectName("Python Console")
|
|
114 |
self.setAllowedAreas(Qt.AllDockWidgetAreas)
|
|
115 |
|
|
116 |
self.widget = QWidget()
|
|
117 |
self.splitter = QSplitter(Qt.Vertical)
|
|
118 |
|
|
119 |
self.layout = QVBoxLayout(self.widget)
|
|
120 |
self.edit = PythonEdit()
|
|
121 |
self.editarea = PythonTextArea()
|
|
122 |
|
|
123 |
self.highlight = PythonHighlighter(self.editarea.document())
|
|
124 |
self.edithighlight = PythonHighlighter(self.edit.document())
|
|
125 |
|
|
126 |
self.splitter.addWidget(self.edit)
|
|
127 |
self.splitter.addWidget(self.editarea)
|
|
128 |
self.layout.addWidget(self.splitter)
|
|
129 |
self.setWidget(self.widget)
|
|
130 |
|
|
131 |
self.setWindowTitle(QCoreApplication.translate("PythonConsole", "Python Console"))
|
|
132 |
# try to restore position from stored main window state
|
|
133 |
if not iface.mainWindow().restoreDockWidget(self):
|
|
134 |
iface.mainWindow().addDockWidget(Qt.BottomDockWidgetArea, self)
|
|
135 |
|
|
136 |
|
|
137 |
def sizeHint(self):
|
|
138 |
return QSize(500, 300)
|
|
139 |
|
|
140 |
def closeEvent(self, event):
|
|
141 |
QWidget.closeEvent(self, event)
|
|
142 |
|
|
143 |
class PythonTextArea(QTextEdit):
|
|
144 |
def __init_(self, parent = None):
|
|
145 |
QTextEdit.__init__(self, parent)
|
|
146 |
|
|
147 |
def keyPressEvent(self, e):
|
|
148 |
if e.modifiers() & Qt.ControlModifier:
|
|
149 |
if e.key() == Qt.Key_Return:
|
|
150 |
lines = QStringList()
|
|
151 |
lines = self.toPlainText().split("\n")
|
|
152 |
#run each command in the edit area
|
|
153 |
for line in lines:
|
|
154 |
_console.edit.insertPlainText(line)
|
|
155 |
_console.edit.runCommand(unicode(line))
|
|
156 |
else:
|
|
157 |
QTextEdit.keyPressEvent(self, e)
|
|
158 |
elif e.key() == Qt.Key_Tab:
|
|
159 |
self.insertPlainText(" " * 4)
|
|
160 |
else:
|
|
161 |
QTextEdit.keyPressEvent(self, e)
|
|
162 |
|
|
163 |
|
|
164 |
|
|
165 |
class PythonHighlighter (QSyntaxHighlighter):
|
|
166 |
#Syntax highlighter for the Python language.
|
96 |
167 |
|
|
168 |
# Python keywords
|
|
169 |
keywords = [
|
|
170 |
'and', 'assert', 'break', 'class', 'continue', 'def',
|
|
171 |
'del', 'elif', 'else', 'except', 'exec', 'finally',
|
|
172 |
'for', 'from', 'global', 'if', 'import', 'in',
|
|
173 |
'is', 'lambda', 'not', 'or', 'pass', 'print',
|
|
174 |
'raise', 'return', 'try', 'while', 'yield',
|
|
175 |
'None', 'True', 'False',
|
|
176 |
]
|
97 |
177 |
|
98 |
|
def sizeHint(self):
|
99 |
|
return QSize(500,300)
|
|
178 |
# Python operators
|
|
179 |
operators = [
|
|
180 |
'=',
|
|
181 |
# Comparison
|
|
182 |
'==', '!=', '<', '<=', '>', '>=',
|
|
183 |
# Arithmetic
|
|
184 |
'\+', '-', '\*', '/', '//', '\%', '\*\*',
|
|
185 |
# In-place
|
|
186 |
'\+=', '-=', '\*=', '/=', '\%=',
|
|
187 |
# Bitwise
|
|
188 |
'\^', '\|', '\&', '\~', '>>', '<<',
|
|
189 |
]
|
100 |
190 |
|
101 |
|
def closeEvent(self, event):
|
102 |
|
QWidget.closeEvent(self, event)
|
|
191 |
# Python braces
|
|
192 |
braces = [
|
|
193 |
'\{', '\}', '\(', '\)', '\[', '\]',
|
|
194 |
]
|
103 |
195 |
|
|
196 |
def __init__(self, document):
|
|
197 |
QSyntaxHighlighter.__init__(self, document)
|
104 |
198 |
|
105 |
|
class ConsoleHighlighter(QSyntaxHighlighter):
|
106 |
|
EDIT_LINE, ERROR, OUTPUT, INIT = range(4)
|
107 |
|
def __init__(self, doc):
|
108 |
|
QSyntaxHighlighter.__init__(self,doc)
|
109 |
|
formats = { self.OUTPUT : Qt.black,
|
110 |
|
self.ERROR : Qt.red,
|
111 |
|
self.EDIT_LINE : Qt.darkGreen,
|
112 |
|
self.INIT : Qt.gray }
|
113 |
|
self.f = {}
|
114 |
|
for tag, color in formats.iteritems():
|
115 |
|
self.f[tag] = QTextCharFormat()
|
116 |
|
self.f[tag].setForeground(color)
|
|
199 |
# Multi-line strings (expression, flag, style)
|
|
200 |
# FIXME: The triple-quotes in these two lines will mess up the
|
|
201 |
# syntax highlighting from this point onward
|
|
202 |
self.tri_single = (QRegExp("'''"), 1, STYLES['string2'])
|
|
203 |
self.tri_double = (QRegExp('"""'), 2, STYLES['string2'])
|
|
204 |
|
|
205 |
rules = []
|
117 |
206 |
|
118 |
|
def highlightBlock(self, txt):
|
119 |
|
size = txt.length()
|
120 |
|
state = self.currentBlockState()
|
121 |
|
if state == self.OUTPUT or state == self.ERROR or state == self.INIT:
|
122 |
|
self.setFormat(0,size, self.f[state])
|
123 |
|
# highlight prompt only
|
124 |
|
if state == self.EDIT_LINE:
|
125 |
|
self.setFormat(0,3, self.f[self.EDIT_LINE])
|
|
207 |
# Keyword, operator, and brace rules
|
|
208 |
rules += [(r'\b%s\b' % w, 0, STYLES['keyword'])
|
|
209 |
for w in PythonHighlighter.keywords]
|
|
210 |
|
|
211 |
rules += [(r'%s' % o, 0, STYLES['operator'])
|
|
212 |
for o in PythonHighlighter.operators]
|
|
213 |
|
|
214 |
rules += [(r'%s' % b, 0, STYLES['brace'])
|
|
215 |
for b in PythonHighlighter.braces]
|
126 |
216 |
|
|
217 |
# All other rules
|
|
218 |
rules += [
|
|
219 |
# 'self'
|
|
220 |
(r'\bself\b', 0, STYLES['self']),
|
127 |
221 |
|
|
222 |
# Double-quoted string, possibly containing escape sequences
|
|
223 |
(r'"[^"\\]*(\\.[^"\\]*)*"', 0, STYLES['string']),
|
|
224 |
# Single-quoted string, possibly containing escape sequences
|
|
225 |
(r"'[^'\\]*(\\.[^'\\]*)*'", 0, STYLES['string']),
|
|
226 |
|
|
227 |
# 'def' followed by an identifier
|
|
228 |
(r'\bdef\b\s*(\w+)', 1, STYLES['defclass']),
|
|
229 |
# 'class' followed by an identifier
|
|
230 |
(r'\bclass\b\s*(\w+)', 1, STYLES['defclass']),
|
|
231 |
|
|
232 |
# From '#' until a newline
|
|
233 |
(r'#[^\n]*', 0, STYLES['comment']),
|
|
234 |
|
|
235 |
# Numeric literals
|
|
236 |
(r'\b[+-]?[0-9]+[lL]?\b', 0, STYLES['numbers']),
|
|
237 |
(r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, STYLES['numbers']),
|
|
238 |
(r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0, STYLES['numbers']),
|
|
239 |
]
|
|
240 |
|
|
241 |
# Build a QRegExp for each pattern
|
|
242 |
self.rules = [(QRegExp(pat), index, fmt)
|
|
243 |
for (pat, index, fmt) in rules]
|
|
244 |
|
|
245 |
|
|
246 |
def highlightBlock(self, text):
|
|
247 |
"""Apply syntax highlighting to the given block of text.
|
|
248 |
"""
|
|
249 |
|
|
250 |
# Do other syntax formatting
|
|
251 |
for expression, nth, format in self.rules:
|
|
252 |
index = expression.indexIn(text, 0)
|
|
253 |
|
|
254 |
while index >= 0:
|
|
255 |
# We actually want the index of the nth match
|
|
256 |
index = expression.pos(nth)
|
|
257 |
length = expression.cap(nth).length()
|
|
258 |
self.setFormat(index, length, format)
|
|
259 |
index = expression.indexIn(text, index + length)
|
|
260 |
|
|
261 |
self.setCurrentBlockState(0)
|
|
262 |
|
|
263 |
# Do multi-line strings
|
|
264 |
in_multiline = self.match_multiline(text, *self.tri_single)
|
|
265 |
if not in_multiline:
|
|
266 |
in_multiline = self.match_multiline(text, *self.tri_double)
|
|
267 |
|
|
268 |
|
|
269 |
def match_multiline(self, text, delimiter, in_state, style):
|
|
270 |
"""Do highlighting of multi-line strings. ``delimiter`` should be a
|
|
271 |
``QRegExp`` for triple-single-quotes or triple-double-quotes, and
|
|
272 |
``in_state`` should be a unique integer to represent the corresponding
|
|
273 |
state changes when inside those strings. Returns True if we're still
|
|
274 |
inside a multi-line string when this function is finished.
|
|
275 |
"""
|
|
276 |
# If inside triple-single quotes, start at 0
|
|
277 |
if self.previousBlockState() == in_state:
|
|
278 |
start = 0
|
|
279 |
add = 0
|
|
280 |
# Otherwise, look for the delimiter on this line
|
|
281 |
else:
|
|
282 |
start = delimiter.indexIn(text)
|
|
283 |
# Move past this match
|
|
284 |
add = delimiter.matchedLength()
|
|
285 |
|
|
286 |
# As long as there's a delimiter match on this line...
|
|
287 |
while start >= 0:
|
|
288 |
# Look for the ending delimiter
|
|
289 |
end = delimiter.indexIn(text, start + add)
|
|
290 |
# Ending delimiter on this line?
|
|
291 |
if end >= add:
|
|
292 |
length = end - start + add + delimiter.matchedLength()
|
|
293 |
self.setCurrentBlockState(0)
|
|
294 |
# No; multi-line string
|
|
295 |
else:
|
|
296 |
self.setCurrentBlockState(in_state)
|
|
297 |
length = text.length() - start + add
|
|
298 |
# Apply formatting
|
|
299 |
self.setFormat(start, length, style)
|
|
300 |
# Look for the next match
|
|
301 |
start = delimiter.indexIn(text, start + length)
|
|
302 |
|
|
303 |
# Return True if still inside a multi-line string, False otherwise
|
|
304 |
if self.currentBlockState() == in_state:
|
|
305 |
return True
|
|
306 |
else:
|
|
307 |
return False
|
|
308 |
|
|
309 |
|
|
310 |
|
128 |
311 |
class PythonEdit(QTextEdit, code.InteractiveInterpreter):
|
|
312 |
def __init__(self, parent = None):
|
|
313 |
QTextEdit.__init__(self, parent)
|
|
314 |
code.InteractiveInterpreter.__init__(self, locals = None)
|
129 |
315 |
|
130 |
|
def __init__(self,parent=None):
|
131 |
|
QTextEdit.__init__(self, parent)
|
132 |
|
code.InteractiveInterpreter.__init__(self, locals=None)
|
|
316 |
self.setTextInteractionFlags(Qt.TextEditorInteraction)
|
|
317 |
self.setAcceptDrops(False)
|
|
318 |
self.setMinimumSize(30, 30)
|
|
319 |
self.setUndoRedoEnabled(False)
|
|
320 |
self.setAcceptRichText(False)
|
|
321 |
monofont = QFont("Monospace")
|
|
322 |
monofont.setStyleHint(QFont.TypeWriter)
|
|
323 |
self.setFont(monofont)
|
133 |
324 |
|
134 |
|
self.setTextInteractionFlags(Qt.TextEditorInteraction)
|
135 |
|
self.setAcceptDrops(False)
|
136 |
|
self.setMinimumSize(30, 30)
|
137 |
|
self.setUndoRedoEnabled(False)
|
138 |
|
self.setAcceptRichText(False)
|
139 |
|
monofont = QFont("Monospace")
|
140 |
|
monofont.setStyleHint(QFont.TypeWriter)
|
141 |
|
self.setFont(monofont)
|
|
325 |
self.buffer = []
|
142 |
326 |
|
143 |
|
self.buffer = []
|
144 |
|
|
145 |
|
self.insertInitText()
|
|
327 |
self.insertInitText()
|
146 |
328 |
|
147 |
|
for line in _init_commands:
|
148 |
|
self.runsource(line)
|
|
329 |
for line in _init_commands:
|
|
330 |
self.runsource(line)
|
149 |
331 |
|
150 |
|
self.displayPrompt(False)
|
|
332 |
self.displayPrompt(False)
|
151 |
333 |
|
152 |
|
self.history = QStringList()
|
153 |
|
self.historyIndex = 0
|
|
334 |
self.history = QStringList()
|
|
335 |
self.historyIndex = 0
|
154 |
336 |
|
155 |
|
self.high = ConsoleHighlighter(self)
|
156 |
|
|
157 |
|
def insertInitText(self):
|
158 |
|
self.insertTaggedText(QCoreApplication.translate("PythonConsole", "To access Quantum GIS environment from this console\n"
|
159 |
|
"use qgis.utils.iface object (instance of QgisInterface class).\n\n"),
|
160 |
|
ConsoleHighlighter.INIT)
|
|
337 |
def insertInitText(self):
|
|
338 |
self.insertTaggedText(QCoreApplication.translate("PythonConsole", "#To access Quantum GIS environment from this console\n"
|
|
339 |
"#use qgis.utils.iface object (instance of QgisInterface class).\n\n"),
|
|
340 |
INIT)
|
161 |
341 |
|
162 |
342 |
|
163 |
|
def clearConsole(self):
|
164 |
|
self.clear()
|
165 |
|
self.insertInitText()
|
|
343 |
def clearConsole(self):
|
|
344 |
self.clear()
|
|
345 |
self.insertInitText()
|
166 |
346 |
|
167 |
|
def displayPrompt(self, more=False):
|
168 |
|
self.currentPrompt = "... " if more else ">>> "
|
169 |
|
self.currentPromptLength = len(self.currentPrompt)
|
170 |
|
self.insertTaggedLine(self.currentPrompt, ConsoleHighlighter.EDIT_LINE)
|
171 |
|
self.moveCursor(QTextCursor.End, QTextCursor.MoveAnchor)
|
|
347 |
def displayPrompt(self, more = False):
|
|
348 |
self.currentPrompt = "... " if more else ">>> "
|
|
349 |
self.currentPromptLength = len(self.currentPrompt)
|
|
350 |
self.insertTaggedLine(self.currentPrompt, EDIT_LINE)
|
|
351 |
self.moveCursor(QTextCursor.End, QTextCursor.MoveAnchor)
|
172 |
352 |
|
173 |
|
def isCursorInEditionZone(self):
|
174 |
|
cursor = self.textCursor()
|
175 |
|
pos = cursor.position()
|
176 |
|
block = self.document().lastBlock()
|
177 |
|
last = block.position() + self.currentPromptLength
|
178 |
|
return pos >= last
|
|
353 |
def isCursorInEditionZone(self):
|
|
354 |
cursor = self.textCursor()
|
|
355 |
pos = cursor.position()
|
|
356 |
block = self.document().lastBlock()
|
|
357 |
last = block.position() + self.currentPromptLength
|
|
358 |
return pos >= last
|
179 |
359 |
|
180 |
|
def currentCommand(self):
|
181 |
|
block = self.cursor.block()
|
182 |
|
text = block.text()
|
183 |
|
return text.right(text.length()-self.currentPromptLength)
|
|
360 |
def currentCommand(self):
|
|
361 |
block = self.cursor.block()
|
|
362 |
text = block.text()
|
|
363 |
return text.right(text.length() - self.currentPromptLength)
|
184 |
364 |
|
185 |
|
def showPrevious(self):
|
|
365 |
def showPrevious(self):
|
186 |
366 |
if self.historyIndex < len(self.history) and not self.history.isEmpty():
|
187 |
367 |
self.cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.MoveAnchor)
|
188 |
368 |
self.cursor.movePosition(QTextCursor.StartOfBlock, QTextCursor.KeepAnchor)
|
... | ... | |
194 |
374 |
else:
|
195 |
375 |
self.insertPlainText(self.history[self.historyIndex])
|
196 |
376 |
|
197 |
|
def showNext(self):
|
|
377 |
def showNext(self):
|
198 |
378 |
if self.historyIndex > 0 and not self.history.isEmpty():
|
199 |
379 |
self.cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.MoveAnchor)
|
200 |
380 |
self.cursor.movePosition(QTextCursor.StartOfBlock, QTextCursor.KeepAnchor)
|
... | ... | |
206 |
386 |
else:
|
207 |
387 |
self.insertPlainText(self.history[self.historyIndex])
|
208 |
388 |
|
209 |
|
def updateHistory(self, command):
|
|
389 |
def updateHistory(self, command):
|
210 |
390 |
if isinstance(command, QStringList):
|
211 |
391 |
for line in command:
|
212 |
392 |
self.history.append(line)
|
... | ... | |
216 |
396 |
self.history.append(command)
|
217 |
397 |
self.historyIndex = len(self.history)
|
218 |
398 |
|
219 |
|
def keyPressEvent(self, e):
|
|
399 |
def keyPressEvent(self, e):
|
220 |
400 |
self.cursor = self.textCursor()
|
221 |
401 |
# if the cursor isn't in the edition zone, don't do anything except Ctrl+C
|
222 |
402 |
if not self.isCursorInEditionZone():
|
... | ... | |
259 |
439 |
elif e.key() == Qt.Key_End:
|
260 |
440 |
anchor = QTextCursor.KeepAnchor if e.modifiers() & Qt.ShiftModifier else QTextCursor.MoveAnchor
|
261 |
441 |
self.cursor.movePosition(QTextCursor.EndOfBlock, anchor, 1)
|
|
442 |
elif e.key() == Qt.Key_Tab:
|
|
443 |
self.insertPlainText(" " * 4)
|
262 |
444 |
# use normal operation for all remaining keys
|
263 |
445 |
else:
|
264 |
446 |
QTextEdit.keyPressEvent(self, e)
|
265 |
447 |
self.setTextCursor(self.cursor)
|
266 |
448 |
self.ensureCursorVisible()
|
267 |
449 |
|
268 |
|
def insertFromMimeData(self, source):
|
|
450 |
def insertFromMimeData(self, source):
|
269 |
451 |
self.cursor = self.textCursor()
|
270 |
452 |
self.cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor, 1)
|
271 |
453 |
self.setTextCursor(self.cursor)
|
... | ... | |
273 |
455 |
pasteList = QStringList()
|
274 |
456 |
pasteList = source.text().split("\n")
|
275 |
457 |
for line in pasteList:
|
276 |
|
self.insertPlainText(line)
|
277 |
|
self.runCommand(unicode(line))
|
|
458 |
self.insertPlainText(line)
|
|
459 |
self.runCommand(unicode(line))
|
278 |
460 |
|
279 |
|
def entered(self):
|
280 |
|
self.cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor)
|
281 |
|
self.setTextCursor(self.cursor)
|
282 |
|
self.runCommand( unicode(self.currentCommand()) )
|
|
461 |
def entered(self):
|
|
462 |
self.cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor)
|
|
463 |
self.setTextCursor(self.cursor)
|
|
464 |
self.runCommand(unicode(self.currentCommand()))
|
283 |
465 |
|
284 |
|
def insertTaggedText(self, txt, tag):
|
|
466 |
def insertTaggedText(self, txt, tag):
|
285 |
467 |
|
286 |
|
if len(txt) > 0 and txt[-1] == '\n': # remove trailing newline to avoid one more empty line
|
287 |
|
txt = txt[0:-1]
|
|
468 |
if len(txt) > 0 and txt[-1] == '\n': # remove trailing newline to avoid one more empty line
|
|
469 |
txt = txt[0:-1]
|
288 |
470 |
|
289 |
|
c = self.textCursor()
|
290 |
|
for line in txt.split('\n'):
|
291 |
|
b = c.block()
|
292 |
|
b.setUserState(tag)
|
293 |
|
c.insertText(line)
|
294 |
|
c.insertBlock()
|
|
471 |
c = self.textCursor()
|
|
472 |
for line in txt.split('\n'):
|
|
473 |
b = c.block()
|
|
474 |
b.setUserState(tag)
|
|
475 |
c.insertText(line)
|
|
476 |
c.insertBlock()
|
295 |
477 |
|
296 |
|
def insertTaggedLine(self, txt, tag):
|
297 |
|
c = self.textCursor()
|
298 |
|
b = c.block()
|
299 |
|
b.setUserState(tag)
|
300 |
|
c.insertText(txt)
|
|
478 |
def insertTaggedLine(self, txt, tag):
|
|
479 |
c = self.textCursor()
|
|
480 |
b = c.block()
|
|
481 |
b.setUserState(tag)
|
|
482 |
c.insertText(txt)
|
301 |
483 |
|
302 |
|
def runCommand(self, cmd):
|
|
484 |
def runCommand(self, cmd):
|
303 |
485 |
|
304 |
|
self.updateHistory(cmd)
|
|
486 |
self.updateHistory(cmd)
|
305 |
487 |
|
306 |
|
self.insertPlainText("\n")
|
|
488 |
self.insertPlainText("\n")
|
307 |
489 |
|
308 |
|
self.buffer.append(cmd)
|
309 |
|
src = "\n".join(self.buffer)
|
310 |
|
more = self.runsource(src, "<input>")
|
311 |
|
if not more:
|
312 |
|
self.buffer = []
|
|
490 |
self.buffer.append(cmd)
|
|
491 |
src = "\n".join(self.buffer)
|
|
492 |
more = self.runsource(src, "<input>")
|
|
493 |
if not more:
|
|
494 |
self.buffer = []
|
313 |
495 |
|
314 |
|
output = sys.stdout.get_and_clean_data()
|
315 |
|
if output:
|
316 |
|
self.insertTaggedText(output, ConsoleHighlighter.OUTPUT)
|
317 |
|
self.displayPrompt(more)
|
|
496 |
output = sys.stdout.get_and_clean_data()
|
|
497 |
if output:
|
|
498 |
self.insertTaggedText(output, OUTPUT)
|
|
499 |
self.displayPrompt(more)
|
318 |
500 |
|
319 |
|
def write(self, txt):
|
320 |
|
""" reimplementation from code.InteractiveInterpreter """
|
321 |
|
self.insertTaggedText(txt, ConsoleHighlighter.ERROR)
|
|
501 |
def write(self, txt):
|
|
502 |
""" reimplementation from code.InteractiveInterpreter """
|
|
503 |
self.insertTaggedText(txt, ERROR)
|
322 |
504 |
|
323 |
505 |
if __name__ == '__main__':
|
324 |
|
a = QApplication(sys.argv)
|
325 |
|
show_console()
|
326 |
|
a.exec_()
|
|
506 |
a = QApplication(sys.argv)
|
|
507 |
show_console()
|
|
508 |
a.exec_()
|