class ShellScintilla(QsciScintilla, code.InteractiveInterpreter): def __init__(self, parent=None): super(ShellScintilla,self).__init__(parent) code.InteractiveInterpreter.__init__(self, locals=None) self.parent = parent self.opening = ['(', '{', '[', "'", '"'] self.closing = [')', '}', ']', "'", '"'] self.settings = QSettings() # Enable non-ascii chars for editor self.setUtf8(True) self.new_input_line = True self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) self.buffer = [] self.displayPrompt(False) for line in _init_commands: self.runsource(line) self.history = [] self.historyIndex = 0 # Read history command file self.readHistoryFile() self.historyDlg = HistoryDialog(self) # Brace matching: enable for a brace immediately before or after # the current position self.setBraceMatching(QsciScintilla.SloppyBraceMatch) self.setMatchedBraceBackgroundColor(QColor("#b7f907")) # Current line visible with special background color self.setCaretWidth(2) self.refreshSettingsShell() # Don't want to see the horizontal scrollbar at all # Use raw message to Scintilla here (all messages are documented # here: http://www.scintilla.org/ScintillaDoc.html) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) # not too small #self.setMinimumSize(500, 300) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER) ## Disable command key ctrl, shift = self.SCMOD_CTRL<<16, self.SCMOD_SHIFT<<16 self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Z')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Y')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L')+ ctrl+shift) ## New QShortcut = ctrl+space/ctrl+alt+space for Autocomplete self.newShortcutCSS = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Space), self) self.newShortcutCAS = QShortcut(QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_Space), self) self.newShortcutCSS.setContext(Qt.WidgetShortcut) self.newShortcutCAS.setContext(Qt.WidgetShortcut) self.newShortcutCAS.activated.connect(self.autoCompleteKeyBinding) self.newShortcutCSS.activated.connect(self.showHistory) def _setMinimumHeight(self): fnt = self.settings.value("pythonConsole/fontfamilytext", "Monospace") fntSize = self.settings.value("pythonConsole/fontsize", 10, type=int) fm = QFontMetrics(QFont(fnt, fntSize)) self.setMinimumHeight(fm.height() + 10) def refreshSettingsShell(self): # Set Python lexer self.setLexers() threshold = self.settings.value("pythonConsole/autoCompThreshold", 2, type=int) self.setAutoCompletionThreshold(threshold) radioButtonSource = self.settings.value("pythonConsole/autoCompleteSource", 'fromAPI') autoCompEnabled = self.settings.value("pythonConsole/autoCompleteEnabled", True, type=bool) if autoCompEnabled: if radioButtonSource == 'fromDoc': self.setAutoCompletionSource(self.AcsDocument) elif radioButtonSource == 'fromAPI': self.setAutoCompletionSource(self.AcsAPIs) elif radioButtonSource == 'fromDocAPI': self.setAutoCompletionSource(self.AcsAll) else: self.setAutoCompletionSource(self.AcsNone) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(Qt.black)) self.setCaretForegroundColor(cursorColor) # Sets minimum height for input area based of font metric self._setMinimumHeight() def showHistory(self): if not self.historyDlg.isVisible(): self.historyDlg.show() self.historyDlg._reloadHistory() self.historyDlg.activateWindow() def autoCompleteKeyBinding(self): radioButtonSource = self.settings.value("pythonConsole/autoCompleteSource", 'fromAPI') autoCompEnabled = self.settings.value("pythonConsole/autoCompleteEnabled", True, type=bool) if autoCompEnabled: if radioButtonSource == 'fromDoc': self.autoCompleteFromDocument() elif radioButtonSource == 'fromAPI': self.autoCompleteFromAPIs() elif radioButtonSource == 'fromDocAPI': self.autoCompleteFromAll() def commandConsole(self, command): if not self.is_cursor_on_last_line(): self.move_cursor_to_end() line, pos = self.getCursorPosition() selCmdLenght = len(self.text(line)) self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() if command == "processing": # import Processing class self.append('import processing') elif command == "qtCore": # import QtCore class self.append('from PyQt4.QtCore import *') elif command == "qtGui": # import QtGui class self.append('from PyQt4.QtGui import *') self.entered() self.move_cursor_to_end() self.setFocus() def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace") fontSize = self.settings.value("pythonConsole/fontsize", 10, type=int) font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) font.setStyleHint(QFont.TypeWriter) font.setStretch(QFont.SemiCondensed) font.setLetterSpacing(QFont.PercentageSpacing, 87.0) font.setBold(False) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor(QColor(self.settings.value("pythonConsole/defaultFontColor", QColor(Qt.black)))) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentFontColor", QColor(Qt.gray))), 1) self.lexer.setColor(QColor(self.settings.value("pythonConsole/keywordFontColor", QColor(Qt.darkGreen))), 5) self.lexer.setColor(QColor(self.settings.value("pythonConsole/classFontColor", QColor(Qt.blue))), 8) self.lexer.setColor(QColor(self.settings.value("pythonConsole/methodFontColor", QColor(Qt.darkGray))), 9) self.lexer.setColor(QColor(self.settings.value("pythonConsole/decorFontColor", QColor(Qt.darkBlue))), 15) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentBlockFontColor", QColor(Qt.gray))), 12) self.lexer.setColor(QColor(self.settings.value("pythonConsole/singleQuoteFontColor", QColor(Qt.blue))), 4) self.lexer.setColor(QColor(self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(Qt.blue))), 3) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(Qt.blue))), 6) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(Qt.blue))), 7) self.lexer.setFont(font, 1) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) for style in range(0, 33): paperColor = QColor(self.settings.value("pythonConsole/paperBackgroundColor", QColor(Qt.white))) self.lexer.setPaper(paperColor, style) self.api = QsciAPIs(self.lexer) chekBoxAPI = self.settings.value("pythonConsole/preloadAPI", True, type=bool) chekBoxPreparedAPI = self.settings.value("pythonConsole/usePreparedAPIFile", False, type=bool) if chekBoxAPI: pap = os.path.join(QgsApplication.pkgDataPath(), "python", "qsci_apis", "pyqgis.pap") self.api.loadPrepared(pap) elif chekBoxPreparedAPI: self.api.loadPrepared(self.settings.value("pythonConsole/preparedAPIFile")) else: apiPath = self.settings.value("pythonConsole/userAPI", []) for i in range(0, len(apiPath)): self.api.load(unicode(apiPath[i])) self.api.prepare() self.lexer.setAPIs(self.api) self.setLexer(self.lexer) ## TODO: show completion list for file and directory def getText(self): """ Get the text as a unicode string. """ value = self.getBytes().decode('utf-8') # print (value) printing can give an error because the console font # may not have all unicode characters return value def getBytes(self): """ Get the text as bytes (utf-8 encoded). This is how the data is stored internally. """ len = self.SendScintilla(self.SCI_GETLENGTH)+1 bb = QByteArray(len,'0') N = self.SendScintilla(self.SCI_GETTEXT, len, bb) return bytes(bb)[:-1] def getTextLength(self): return self.SendScintilla(QsciScintilla.SCI_GETLENGTH) def get_end_pos(self): """Return (line, index) position of the last character""" line = self.lines() - 1 return (line, len(self.text(line))) def is_cursor_at_end(self): """Return True if cursor is at the end of text""" cline, cindex = self.getCursorPosition() return (cline, cindex) == self.get_end_pos() def move_cursor_to_end(self): """Move cursor to end of text""" line, index = self.get_end_pos() self.setCursorPosition(line, index) self.ensureCursorVisible() self.ensureLineVisible(line) def is_cursor_on_last_line(self): """Return True if cursor is on the last line""" cline, _ = self.getCursorPosition() return cline == self.lines() - 1 def is_cursor_on_edition_zone(self): """ Return True if the cursor is in the edition zone """ cline, cindex = self.getCursorPosition() return cline == self.lines() - 1 and cindex >= 4 def new_prompt(self, prompt): """ Print a new prompt and save its (line, index) position """ self.write(prompt, prompt=True) # now we update our cursor giving end of prompt line, index = self.getCursorPosition() self.ensureCursorVisible() self.ensureLineVisible(line) def displayPrompt(self, more=False): self.append("... ") if more else self.append(">>> ") self.move_cursor_to_end() def updateHistory(self, command): if isinstance(command, list): for line in command: self.history.append(line) elif not command == "": if len(self.history) <= 0 or \ not command == self.history[-1]: self.history.append(command) self.historyIndex = len(self.history) def writeHistoryFile(self, fromCloseConsole=False): ok = False try: wH = codecs.open(_historyFile, 'w', encoding='utf-8') for s in self.history: wH.write(s + '\n') ok = True except: raise wH.close() if ok and not fromCloseConsole: msgText = QCoreApplication.translate('PythonConsole', 'History saved successfully.') self.parent.callWidgetMessageBar(msgText) def readHistoryFile(self): fileExist = QFile.exists(_historyFile) if fileExist: rH = codecs.open(_historyFile, 'r', encoding='utf-8') for line in rH: if line != "\n": l = line.rstrip('\n') self.updateHistory(l) else: return def clearHistory(self, clearSession=False): if clearSession: self.history = [] msgText = QCoreApplication.translate('PythonConsole', 'Session and file history cleared successfully.') self.parent.callWidgetMessageBar(msgText) return ok = False try: cH = codecs.open(_historyFile, 'w', encoding='utf-8') ok = True except: raise cH.close() if ok: msgText = QCoreApplication.translate('PythonConsole', 'History cleared successfully.') self.parent.callWidgetMessageBar(msgText) def clearHistorySession(self): self.clearHistory(True) def showPrevious(self): if self.historyIndex < len(self.history) and self.history: line, pos = self.getCursorPosition() selCmdLenght = len(self.text(line)) self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() self.historyIndex += 1 if self.historyIndex == len(self.history): self.insert("") pass else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def showNext(self): if self.historyIndex > 0 and self.history: line, pos = self.getCursorPosition() selCmdLenght = len(self.text(line)) self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() self.historyIndex -= 1 if self.historyIndex == len(self.history): self.insert("") else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def keyPressEvent(self, e): startLine, startPos, endLine, endPos = self.getSelection() # handle invalid cursor position and multiline selections if not self.is_cursor_on_edition_zone() or startLine < endLine: # allow to copy and select if e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier): if e.key() in (Qt.Key_C, Qt.Key_A): QsciScintilla.keyPressEvent(self, e) return # allow selection if e.modifiers() & Qt.ShiftModifier: if e.key() in (Qt.Key_Left, Qt.Key_Right, Qt.Key_Home, Qt.Key_End): QsciScintilla.keyPressEvent(self, e) return # all other keystrokes get sent to the input line self.move_cursor_to_end() line, index = self.getCursorPosition() cmd = self.text(line) if e.key() in (Qt.Key_Return, Qt.Key_Enter) and not self.isListActive(): self.entered() elif e.key() in (Qt.Key_Left, Qt.Key_Home): QsciScintilla.keyPressEvent(self, e) # check whether the cursor is moved out of the edition zone newline, newindex = self.getCursorPosition() if newline < line or newindex < 4: # fix selection and the cursor position if self.hasSelectedText(): self.setSelection(line, self.getSelection()[3], line, 4) else: self.setCursorPosition(line, 4) elif e.key() in (Qt.Key_Backspace, Qt.Key_Delete): QsciScintilla.keyPressEvent(self, e) # check whether the cursor is moved out of the edition zone _, newindex = self.getCursorPosition() if newindex < 4: # restore the prompt chars (if removed) and # fix the cursor position self.insert( cmd[:3-newindex] + " " ) self.setCursorPosition(line, 4) self.recolor() elif (e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier) and \ e.key() == Qt.Key_V) or \ (e.modifiers() & Qt.ShiftModifier and e.key() == Qt.Key_Insert): self.paste() e.accept() elif e.key() == Qt.Key_Down and not self.isListActive(): self.showPrevious() elif e.key() == Qt.Key_Up and not self.isListActive(): self.showNext() ## TODO: press event for auto-completion file directory else: t = unicode(e.text()) self.autoCloseBracket = self.settings.value("pythonConsole/autoCloseBracket", False, type=bool) self.autoImport = self.settings.value("pythonConsole/autoInsertionImport", True, type=bool) txt = cmd[:index].replace('>>> ', '').replace('... ', '') ## Close bracket automatically if t in self.opening and self.autoCloseBracket: i = self.opening.index(t) if self.hasSelectedText() and startPos != 0: selText = self.selectedText() self.removeSelectedText() self.insert(self.opening[i] + selText + self.closing[i]) self.setCursorPosition(endLine, endPos+2) return elif t == '(' and (re.match(r'^[ \t]*def \w+$', txt) \ or re.match(r'^[ \t]*class \w+$', txt)): self.insert('):') else: self.insert(self.closing[i]) ## FIXES #8392 (automatically removes the redundant char ## when autoclosing brackets option is enabled) elif t in [')', ']', '}'] and self.autoCloseBracket: txt = self.text(line) try: if txt[index-1] in self.opening and t == txt[index]: self.setCursorPosition(line, index+1) self.SendScintilla(QsciScintilla.SCI_DELETEBACK) except IndexError: pass elif t == ' ' and self.autoImport: ptrn = r'^[ \t]*from [\w.]+$' if re.match(ptrn, txt): self.insert(' import') self.setCursorPosition(line, index + 7) QsciScintilla.keyPressEvent(self, e) def contextMenuEvent(self, e): menu = QMenu(self) subMenu = QMenu(menu) titleHistoryMenu = QCoreApplication.translate("PythonConsole", "Command History") subMenu.setTitle(titleHistoryMenu) showHistoryAction = subMenu.addAction( QCoreApplication.translate("PythonConsole", "Show"), self.showHistory, 'Ctrl+Shift+SPACE') subMenu.addSeparator() saveHistoryAction = subMenu.addAction( QCoreApplication.translate("PythonConsole", "Save"), self.writeHistoryFile) subMenu.addSeparator() clearHistoryAction = subMenu.addAction( QCoreApplication.translate("PythonConsole", "Clear File"), self.clearHistory) clearSessHistoryAction = subMenu.addAction( QCoreApplication.translate("PythonConsole", "Clear Session"), self.clearHistorySession) menu.addMenu(subMenu) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) pasteAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Paste"), self.paste, QKeySequence.Paste) copyAction.setEnabled(False) pasteAction.setEnabled(False) if self.hasSelectedText(): copyAction.setEnabled(True) if QApplication.clipboard().text(): pasteAction.setEnabled(True) action = menu.exec_(self.mapToGlobal(e.pos())) def mousePressEvent(self, e): """ Re-implemented to handle the mouse press event. e: the mouse press event (QMouseEvent) """ self.setFocus() if e.button() == Qt.MidButton: stringSel = unicode(QApplication.clipboard().text(QClipboard.Selection)) if not self.is_cursor_on_last_line(): self.move_cursor_to_end() self.insertFromDropPaste(stringSel) e.accept() else: QsciScintilla.mousePressEvent(self, e) def paste(self): """ Method to display data from the clipboard. XXX: It should reimplement the virtual QScintilla.paste method, but it seems not used by QScintilla code. """ stringPaste = unicode(QApplication.clipboard().text()) if self.is_cursor_on_last_line(): if self.hasSelectedText(): self.removeSelectedText() else: self.move_cursor_to_end() self.insertFromDropPaste(stringPaste) ## Drag and drop def dropEvent(self, e): if e.mimeData().hasText(): stringDrag = e.mimeData().text() self.insertFromDropPaste(stringDrag) self.setFocus() e.setDropAction(Qt.CopyAction) e.accept() else: QsciScintillaCompat.dropEvent(self, e) def insertFromDropPaste(self, textDP): pasteList = unicode(textDP).splitlines() if pasteList: for line in pasteList[:-1]: cleanLine = line.replace(">>> ", "").replace("... ", "") self.insert(unicode(cleanLine)) self.move_cursor_to_end() self.runCommand(unicode(self.currentCommand())) if pasteList[-1] != "": line = pasteList[-1] cleanLine = line.replace(">>> ", "").replace("... ", "") self.insert(unicode(cleanLine)) self.move_cursor_to_end() def insertTextFromFile(self, listOpenFile): for line in listOpenFile[:-1]: self.append(line) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) self.runCommand(unicode(self.currentCommand())) self.append(unicode(listOpenFile[-1])) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def entered(self): self.move_cursor_to_end() self.runCommand( unicode(self.currentCommand()) ) self.setFocus() self.move_cursor_to_end() def currentCommand(self): linenr, index = self.getCursorPosition() txtLength = len(self.text(linenr)) string = self.text() cmdLine = string[4:] cmd = unicode(cmdLine) return cmd def runCommand(self, cmd): self.writeCMD(cmd) import webbrowser self.updateHistory(cmd) if cmd in ('_pyqgis', '_api'): if cmd == '_pyqgis': webbrowser.open( "http://qgis.org/pyqgis-cookbook/" ) elif cmd == '_api': webbrowser.open( "http://qgis.org/api/" ) more = False else: self.buffer.append(cmd) src = u"\n".join(self.buffer) more = self.runsource(src, "<input>") if not more: self.buffer = [] ## prevents to commands with more lines to break the console ## in the case they have a eol different from '\n' self.setText('') self.move_cursor_to_end() self.displayPrompt(more) def write(self, txt): sys.stderr.write(txt) def writeCMD(self, txt): if len(txt) > 0: getCmdString = self.text() prompt = getCmdString[0:4] sys.stdout.write(prompt+txt+'\n')
class ShellOutputScintilla(QsciScintilla): def __init__(self, parent=None): super(ShellOutputScintilla,self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.setLexers() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor("#fcf3ed")) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemeted copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate("PythonConsole", "Python %1 on %2\n" "## Type help(iface) for more info and list of methods.\n").arg(sys.version, socket.gethostname()) initText = self.setText(txtInit) def refreshLexerProperties(self): self.setLexers() def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace").toString() fontSize = self.settings.value("pythonConsole/fontsize", 10).toInt()[0] font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) self.lexer.setDefaultFont(font) self.lexer.setColor(Qt.red, 1) self.lexer.setColor(Qt.darkGreen, 5) self.lexer.setColor(Qt.darkBlue, 15) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/iconRunConsole.png") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.png") iconHideTool = QgsApplication.getThemeIcon("console/iconHideToolConsole.png") iconSettings = QgsApplication.getThemeIcon("console/iconSettingsConsole.png") hideToolBar = menu.addAction(iconHideTool, "Hide/Show Toolbar", self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction("Show Editor", self.showEditor) menu.addSeparator() runAction = menu.addAction(iconRun, "Enter Selected", self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(iconClear, "Clear console", self.clearConsole) menu.addSeparator() copyAction = menu.addAction("Copy", self.copy, QKeySequence.Copy) menu.addSeparator() selectAllAction = menu.addAction("Select All", self.selectAll, QKeySequence.SelectAll) menu.addSeparator() settingsDialog = menu.addAction(iconSettings, "Settings", self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) action = menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = unicode(self.selectedText()) text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: self.emit(SIGNAL("keyboard_interrupt()")) def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if txt.length() and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, QgsMessageBar.INFO, timeout)
class PythonEdit(QsciScintilla, code.InteractiveInterpreter): def __init__(self, parent=None): #QsciScintilla.__init__(self, parent) super(PythonEdit, self).__init__(parent) code.InteractiveInterpreter.__init__(self, locals=None) self.parent = parent # Enable non-ascii chars for editor self.setUtf8(True) self.new_input_line = True self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) self.buffer = [] self.displayPrompt(False) for line in _init_commands: self.runsource(line) self.history = QStringList() self.historyIndex = 0 # Read history command file self.readHistoryFile() # Brace matching: enable for a brace immediately before or after # the current position self.setBraceMatching(QsciScintilla.SloppyBraceMatch) #self.moveToMatchingBrace() #self.selectToMatchingBrace() # Current line visible with special background color #self.setCaretLineVisible(True) #self.setCaretLineBackgroundColor(QColor("#ffe4e4")) self.setCaretWidth(2) # Set Python lexer self.setLexers() # Indentation #self.setAutoIndent(True) #self.setIndentationsUseTabs(False) #self.setIndentationWidth(4) #self.setTabIndents(True) #self.setBackspaceUnindents(True) #self.setTabWidth(4) self.setAutoCompletionThreshold(2) self.setAutoCompletionSource(self.AcsAPIs) # Don't want to see the horizontal scrollbar at all # Use raw message to Scintilla here (all messages are documented # here: http://www.scintilla.org/ScintillaDoc.html) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) # not too small #self.setMinimumSize(500, 300) self.setMinimumHeight(20) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER) ## Disable command key ctrl, shift = self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16 self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Z') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Y') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl + shift) ## New QShortcut = ctrl+space/ctrl+alt+space for Autocomplete self.newShortcutCS = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Space), self) self.newShortcutCAS = QShortcut( QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_Space), self) self.newShortcutCS.activated.connect(self.autoComplete) self.newShortcutCAS.activated.connect(self.showHistory) self.connect(self, SIGNAL('userListActivated(int, const QString)'), self.completion_list_selected) def showHistory(self): self.showUserList(1, QStringList(self.history)) def autoComplete(self): self.autoCompleteFromAll() def commandConsole(self, command): if not self.is_cursor_on_last_line(): self.move_cursor_to_end() line, pos = self.getCursorPosition() selCmdLenght = self.text(line).length() self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() if command == "sextante": # import Sextante class self.append('import sextante') elif command == "qtCore": # import QtCore class self.append('from PyQt4.QtCore import *') elif command == "qtGui": # import QtGui class self.append('from PyQt4.QtGui import *') self.entered() self.move_cursor_to_end() self.setFocus() def setLexers(self): self.lexer = QsciLexerPython() settings = QSettings() loadFont = settings.value("pythonConsole/fontfamilytext", "Monospace").toString() fontSize = settings.value("pythonConsole/fontsize", 10).toInt()[0] font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) font.setStyleHint(QFont.TypeWriter) font.setStretch(QFont.SemiCondensed) font.setLetterSpacing(QFont.PercentageSpacing, 87.0) font.setBold(False) self.lexer.setDefaultFont(font) self.lexer.setColor(Qt.red, 1) self.lexer.setColor(Qt.darkGreen, 5) self.lexer.setColor(Qt.darkBlue, 15) self.lexer.setFont(font, 1) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.api = QsciAPIs(self.lexer) chekBoxAPI = settings.value("pythonConsole/preloadAPI", True).toBool() if chekBoxAPI: self.api.loadPrepared(QgsApplication.pkgDataPath() + "/python/qsci_apis/pyqgis_master.pap") else: apiPath = settings.value("pythonConsole/userAPI").toStringList() for i in range(0, len(apiPath)): self.api.load(QString(unicode(apiPath[i]))) self.api.prepare() self.lexer.setAPIs(self.api) self.setLexer(self.lexer) ## TODO: show completion list for file and directory def completion_list_selected(self, id, txt): if id == 1: txt = unicode(txt) # get current cursor position line, pos = self.getCursorPosition() selCmdLength = self.text(line).length() # select typed text self.setSelection(line, 4, line, selCmdLength) self.removeSelectedText() self.insert(txt) def getText(self): """ Get the text as a unicode string. """ value = self.getBytes().decode('utf-8') # print (value) printing can give an error because the console font # may not have all unicode characters return value def getBytes(self): """ Get the text as bytes (utf-8 encoded). This is how the data is stored internally. """ len = self.SendScintilla(self.SCI_GETLENGTH) + 1 bb = QByteArray(len, '0') N = self.SendScintilla(self.SCI_GETTEXT, len, bb) return bytes(bb)[:-1] def getTextLength(self): return self.SendScintilla(QsciScintilla.SCI_GETLENGTH) def get_end_pos(self): """Return (line, index) position of the last character""" line = self.lines() - 1 return (line, self.text(line).length()) def is_cursor_at_end(self): """Return True if cursor is at the end of text""" cline, cindex = self.getCursorPosition() return (cline, cindex) == self.get_end_pos() def move_cursor_to_end(self): """Move cursor to end of text""" line, index = self.get_end_pos() self.setCursorPosition(line, index) self.ensureCursorVisible() self.ensureLineVisible(line) # def on_new_line(self): # """On new input line""" # self.move_cursor_to_end() # self.new_input_line = False def is_cursor_on_last_line(self): """Return True if cursor is on the last line""" cline, _ = self.getCursorPosition() return cline == self.lines() - 1 def is_cursor_on_edition_zone(self): """ Return True if the cursor is in the edition zone """ cline, cindex = self.getCursorPosition() return cline == self.lines() - 1 and cindex >= 4 def new_prompt(self, prompt): """ Print a new prompt and save its (line, index) position """ self.write(prompt, prompt=True) # now we update our cursor giving end of prompt line, index = self.getCursorPosition() self.ensureCursorVisible() self.ensureLineVisible(line) def refreshLexerProperties(self): self.setLexers() # def check_selection(self): # """ # Check if selected text is r/w, # otherwise remove read-only parts of selection # """ # #if self.current_prompt_pos is None: # #self.move_cursor_to_end() # #return # line_from, index_from, line_to, index_to = self.getSelection() # pline, pindex = self.getCursorPosition() # if line_from < pline or \ # (line_from == pline and index_from < pindex): # self.setSelection(pline, pindex, line_to, index_to) def displayPrompt(self, more=False): self.append("... ") if more else self.append(">>> ") self.move_cursor_to_end() def updateHistory(self, command): if isinstance(command, QStringList): for line in command: self.history.append(line) elif not command == "": if len(self.history) <= 0 or \ not command == self.history[-1]: self.history.append(command) self.historyIndex = len(self.history) def writeHistoryFile(self): wH = open(_historyFile, 'w') for s in self.history: wH.write(s + '\n') wH.close() def readHistoryFile(self): fileExist = QFile.exists(_historyFile) if fileExist: rH = open(_historyFile, 'r') for line in rH: if line != "\n": l = line.rstrip('\n') self.updateHistory(l) else: return def clearHistoryFile(self): cH = open(_historyFile, 'w') cH.close() def showPrevious(self): if self.historyIndex < len( self.history) and not self.history.isEmpty(): line, pos = self.getCursorPosition() selCmdLenght = self.text(line).length() self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() self.historyIndex += 1 if self.historyIndex == len(self.history): self.insert("") pass else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def showNext(self): if self.historyIndex > 0 and not self.history.isEmpty(): line, pos = self.getCursorPosition() selCmdLenght = self.text(line).length() self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() self.historyIndex -= 1 if self.historyIndex == len(self.history): self.insert("") else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def keyPressEvent(self, e): startLine, _, endLine, _ = self.getSelection() # handle invalid cursor position and multiline selections if not self.is_cursor_on_edition_zone() or startLine < endLine: # allow to copy and select if e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier): if e.key() in (Qt.Key_C, Qt.Key_A): QsciScintilla.keyPressEvent(self, e) return # allow selection if e.modifiers() & Qt.ShiftModifier: if e.key() in (Qt.Key_Left, Qt.Key_Right, Qt.Key_Home, Qt.Key_End): QsciScintilla.keyPressEvent(self, e) return # all other keystrokes get sent to the input line self.move_cursor_to_end() line, index = self.getCursorPosition() cmd = self.text(line) if e.key() in (Qt.Key_Return, Qt.Key_Enter) and not self.isListActive(): self.entered() elif e.key() in (Qt.Key_Left, Qt.Key_Home): QsciScintilla.keyPressEvent(self, e) # check whether the cursor is moved out of the edition zone newline, newindex = self.getCursorPosition() if newline < line or newindex < 4: # fix selection and the cursor position if self.hasSelectedText(): self.setSelection(line, self.getSelection()[3], line, 4) else: self.setCursorPosition(line, 4) elif e.key() in (Qt.Key_Backspace, Qt.Key_Delete): QsciScintilla.keyPressEvent(self, e) # check whether the cursor is moved out of the edition zone _, newindex = self.getCursorPosition() if newindex < 4: # restore the prompt chars (if removed) and # fix the cursor position self.insert(cmd[:3 - newindex] + " ") self.setCursorPosition(line, 4) self.recolor() elif e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier) and \ e.key() == Qt.Key_V: self.paste() e.accept() elif e.key() == Qt.Key_Down and not self.isListActive(): self.showPrevious() elif e.key() == Qt.Key_Up and not self.isListActive(): self.showNext() ## TODO: press event for auto-completion file directory else: QsciScintilla.keyPressEvent(self, e) def contextMenuEvent(self, e): menu = QMenu(self) copyAction = menu.addAction("Copy", self.copy, QKeySequence.Copy) pasteAction = menu.addAction("Paste", self.paste, QKeySequence.Paste) copyAction.setEnabled(False) pasteAction.setEnabled(False) if self.hasSelectedText(): copyAction.setEnabled(True) if QApplication.clipboard().text() != "": pasteAction.setEnabled(True) action = menu.exec_(self.mapToGlobal(e.pos())) def mousePressEvent(self, e): """ Re-implemented to handle the mouse press event. e: the mouse press event (QMouseEvent) """ self.setFocus() if e.button() == Qt.MidButton: stringSel = unicode(QApplication.clipboard().text( QClipboard.Selection)) if not self.is_cursor_on_last_line(): self.move_cursor_to_end() self.insertFromDropPaste(stringSel) e.accept() else: QsciScintilla.mousePressEvent(self, e) def paste(self): """ Method to display data from the clipboard. XXX: It should reimplement the virtual QScintilla.paste method, but it seems not used by QScintilla code. """ stringPaste = unicode(QApplication.clipboard().text()) if self.is_cursor_on_last_line(): if self.hasSelectedText(): self.removeSelectedText() else: self.move_cursor_to_end() self.insertFromDropPaste(stringPaste) ## Drag and drop def dropEvent(self, e): if e.mimeData().hasText(): stringDrag = e.mimeData().text() self.insertFromDropPaste(stringDrag) self.setFocus() e.setDropAction(Qt.MoveAction) e.accept() else: QsciScintillaCompat.dropEvent(self, e) def insertFromDropPaste(self, textDP): pasteList = textDP.split("\n") for line in pasteList[:-1]: line.replace(">>> ", "").replace("... ", "") self.insert(line) self.move_cursor_to_end() self.runCommand(unicode(self.currentCommand())) if pasteList[-1] != "": line = pasteList[-1] line.replace(">>> ", "").replace("... ", "") self.insert(unicode(line)) self.move_cursor_to_end() # def getTextFromEditor(self): # text = self.text() # textList = text.split("\n") # return textList def insertTextFromFile(self, listOpenFile): for line in listOpenFile[:-1]: self.append(line) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) self.runCommand(unicode(self.currentCommand())) self.append(unicode(listOpenFile[-1])) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def entered(self): self.move_cursor_to_end() self.runCommand(unicode(self.currentCommand())) self.setFocus() self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER) def currentCommand(self): linenr, index = self.getCursorPosition() #for i in range(0, linenr): txtLength = self.text(linenr).length() string = self.text() cmdLine = string.right(txtLength - 4) cmd = unicode(cmdLine) return cmd def runCommand(self, cmd): self.write_stdout(cmd) import webbrowser self.updateHistory(cmd) line, pos = self.getCursorPosition() selCmdLenght = self.text(line).length() self.setSelection(line, 0, line, selCmdLenght) self.removeSelectedText() if cmd in ('_save', '_clear', '_clearAll', '_pyqgis', '_api'): if cmd == '_save': self.writeHistoryFile() msgText = QCoreApplication.translate( 'PythonConsole', 'History saved successfully.') elif cmd == '_clear': self.clearHistoryFile() msgText = QCoreApplication.translate( 'PythonConsole', 'History cleared successfully.') elif cmd == '_clearAll': self.history = QStringList() self.clearHistoryFile() msgText = QCoreApplication.translate( 'PythonConsole', 'Session and file history cleared successfully.') elif cmd == '_pyqgis': webbrowser.open("http://www.qgis.org/pyqgis-cookbook/") elif cmd == '_api': webbrowser.open("http://www.qgis.org/api/") if msgText: self.parent.callWidgetMessageBar(msgText) self.displayPrompt(False) else: self.buffer.append(cmd) src = u"\n".join(self.buffer) more = self.runsource(src, "<input>") if not more: self.buffer = [] self.move_cursor_to_end() self.displayPrompt(more) def write(self, txt): sys.stderr.write(txt) def write_stdout(self, txt): if len(txt) > 0: getCmdString = self.text() prompt = getCmdString[0:4] sys.stdout.write(prompt + txt + '\n')
class EditorOutput(QsciScintilla): def __init__(self, parent=None): #QsciScintilla.__init__(self, parent) super(EditorOutput,self).__init__(parent) self.parent = parent self.edit = self.parent.edit # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "traceback") self.insertInitText() self.setLexers() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor("#fcf3ed")) self.setMinimumHeight(120) # Folding #self.setFolding(QsciScintilla.BoxedTreeFoldStyle) #self.setFoldMarginColors(QColor("#99CC66"),QColor("#333300")) #self.setWrapMode(QsciScintilla.WrapCharacter) ## Edge Mode #self.setEdgeMode(QsciScintilla.EdgeLine) #self.setEdgeColumn(80) #self.setEdgeColor(QColor("#FF0000")) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runShortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runShortcut.activated.connect(self.enteredSelected) # Reimplemeted copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate("PythonConsole", "## To access Quantum GIS environment from this console\n" "## use qgis.utils.iface object (instance of QgisInterface class). Read help for more info.\n\n") initText = self.setText(txtInit) def refreshLexerProperties(self): self.setLexers() def setLexers(self): self.lexer = QsciLexerPython() settings = QSettings() loadFont = settings.value("pythonConsole/fontfamilytext", "Monospace").toString() fontSize = settings.value("pythonConsole/fontsize", 10).toInt()[0] font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) self.lexer.setDefaultFont(font) self.lexer.setColor(Qt.red, 1) self.lexer.setColor(Qt.darkGreen, 5) self.lexer.setColor(Qt.darkBlue, 15) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.setLexer(self.lexer) def getTextFromEditor(self): text = self.text() textList = text.split("\n") return textList def clearConsole(self): #self.SendScintilla(QsciScintilla.SCI_CLEARALL) self.setText('') self.insertInitText() self.edit.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QIcon(":/images/console/iconRunConsole.png") iconPastebin = QIcon(":/images/console/iconCodepadConsole.png") iconClear = QIcon(":/images/console/iconClearConsole.png") iconHideTool = QIcon(":/images/console/iconHideToolConsole.png") hideToolBar = menu.addAction(iconHideTool, "Hide/Show Toolbar", self.hideToolBar) menu.addSeparator() runAction = menu.addAction(iconRun, "Enter Selected", self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(iconClear, "Clear console", self.clearConsole) menu.addSeparator() copyAction = menu.addAction("Copy", self.copy, QKeySequence.Copy) pastebinAction = menu.addAction(iconPastebin, "Share on codepad", self.pastebin) menu.addSeparator() selectAllAction = menu.addAction("Select All", self.selectAll, QKeySequence.SelectAll) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) pastebinAction.setEnabled(False) selectAllAction.setEnabled(False) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) pastebinAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) action = menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.edit.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = unicode(self.selectedText()) text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: self.emit(SIGNAL("keyboard_interrupt()")) def enteredSelected(self): cmd = self.selectedText() self.edit.insertFromDropPaste(cmd) self.edit.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if txt.length() and txt >= " ": self.edit.append(txt) self.edit.move_cursor_to_end() self.edit.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def pastebin(self): import urllib2, urllib listText = self.selectedText().split('\n') getCmd = [] for strLine in listText: if strLine != "": #if s[0:3] in (">>>", "..."): # filter for special command (_save,_clear) and comment if strLine[4] != "_" and strLine[:2] != "##": strLine.replace(">>> ", "").replace("... ", "") getCmd.append(unicode(strLine)) pasteText= u"\n".join(getCmd) url = 'http://codepad.org' values = {'lang' : 'Python', 'code' : pasteText, 'submit':'Submit'} try: response = urllib2.urlopen(url, urllib.urlencode(values)) url = response.read() for href in url.split("</a>"): if "Link:" in href: ind=href.index('Link:') found = href[ind+5:] for i in found.split('">'): if '<a href=' in i: link = i.replace('<a href="',"").strip() if link: QApplication.clipboard().setText(link) print "## URL copied to clipboard ##" except urllib2.URLError, e: print "## Connection error ##" print "## " + str(e.args) + " ##"
class ShellOutputScintilla(QsciScintilla): def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretWidth(0) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemeted copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate("PythonConsole", "Python Console \n" "Use iface to access QGIS API interface or Type help(iface) for more info") ## some translation string for the console header ends without '\n' ## and the first command in console will be appended at the header text. ## The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def refreshSettingsOutput(self): # Set Python lexer self.setLexers() caretLineColor = self.settings.value("pythonConsole/caretLineColor", QColor("#fcf3ed")) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(Qt.black)) self.setCaretLineBackgroundColor(caretLineColor) self.setCaretForegroundColor(cursorColor) def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace") fontSize = self.settings.value("pythonConsole/fontsize", 10, type=int) font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) font.setStyleHint(QFont.TypeWriter) font.setStretch(QFont.SemiCondensed) font.setLetterSpacing(QFont.PercentageSpacing, 87.0) font.setBold(False) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor(QColor(self.settings.value("pythonConsole/defaultFontColor", QColor(Qt.black)))) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentFontColor", QColor(Qt.gray))), 1) self.lexer.setColor(QColor(self.settings.value("pythonConsole/keywordFontColor", QColor(Qt.darkGreen))), 5) self.lexer.setColor(QColor(self.settings.value("pythonConsole/classFontColor", QColor(Qt.blue))), 8) self.lexer.setColor(QColor(self.settings.value("pythonConsole/methodFontColor", QColor(Qt.darkGray))), 9) self.lexer.setColor(QColor(self.settings.value("pythonConsole/decorFontColor", QColor(Qt.darkBlue))), 15) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentBlockFontColor", QColor(Qt.gray))), 12) self.lexer.setColor(QColor(self.settings.value("pythonConsole/singleQuoteFontColor", QColor(Qt.blue))), 4) self.lexer.setColor(QColor(self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(Qt.blue))), 3) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(Qt.blue))), 6) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(Qt.blue))), 7) self.lexer.setColor(QColor(Qt.red), 14) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) for style in range(0, 33): paperColor = QColor(self.settings.value("pythonConsole/paperBackgroundColor", QColor(Qt.white))) self.lexer.setPaper(paperColor, style) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/iconRunConsole.png") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.png") iconHideTool = QgsApplication.getThemeIcon("console/iconHideToolConsole.png") iconSettings = QgsApplication.getThemeIcon("console/iconSettingsConsole.png") menu.addAction(iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction(iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(iconClear, QCoreApplication.translate("PythonConsole", "Clear console"), self.clearConsole) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) menu.addSeparator() selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction(iconSettings, QCoreApplication.translate("PythonConsole", "Settings"), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = unicode(self.selectedText()) text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: self.emit(SIGNAL("keyboard_interrupt()")) def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, QgsMessageBar.INFO, timeout)
class Editor(QsciScintilla): def __init__(self, parent=None): super(Editor,self).__init__(parent) self.parent = parent self.settings = QSettings() # Enable non-ascii chars for editor self.setUtf8(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers #fm = QFontMetrics(font) #fontmetrics = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor("#fcf3ed")) # Clickable margin 1 for showing markers # self.setMarginSensitivity(1, True) # self.connect(self, # SIGNAL('marginClicked(int, int, Qt::KeyboardModifiers)'), # self.on_margin_clicked) # self.markerDefine(QsciScintilla.RightArrow, # self.ARROW_MARKER_NUM) # self.setMarkerBackgroundColor(QColor("#ee1111"), # self.ARROW_MARKER_NUM) self.setMinimumHeight(120) #self.setMinimumWidth(300) # Folding self.setFolding(QsciScintilla.PlainFoldStyle) self.setFoldMarginColors(QColor("#f4f4f4"),QColor("#f4f4f4")) #self.setWrapMode(QsciScintilla.WrapCharacter) ## Edge Mode self.setEdgeMode(QsciScintilla.EdgeLine) self.setEdgeColumn(80) self.setEdgeColor(QColor("#FF0000")) #self.setWrapMode(QsciScintilla.WrapCharacter) self.setWhitespaceVisibility(QsciScintilla.WsVisibleAfterIndent) #self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.settingsEditor() # Annotations #self.setAnnotationDisplay(QsciScintilla.ANNOTATION_BOXED) # Indentation self.setAutoIndent(True) self.setIndentationsUseTabs(False) self.setIndentationWidth(4) self.setTabIndents(True) self.setBackspaceUnindents(True) self.setTabWidth(4) self.setIndentationGuides(True) ## Disable command key ctrl, shift = self.SCMOD_CTRL<<16, self.SCMOD_SHIFT<<16 self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D')+ ctrl) #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Z')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Y')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L')+ ctrl+shift) ## New QShortcut = ctrl+space/ctrl+alt+space for Autocomplete self.newShortcutCS = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Space), self) self.newShortcutCS.setContext(Qt.WidgetShortcut) self.newShortcutCS.activated.connect(self.autoCompleteKeyBinding) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.runSelectedCode) self.runScriptScut = QShortcut(QKeySequence(Qt.SHIFT + Qt.CTRL + Qt.Key_E), self) self.runScriptScut.setContext(Qt.WidgetShortcut) self.runScriptScut.activated.connect(self.runScriptCode) self.commentScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_3), self) self.commentScut.setContext(Qt.WidgetShortcut) self.commentScut.activated.connect(self.parent.pc.commentCode) self.uncommentScut = QShortcut(QKeySequence(Qt.SHIFT + Qt.CTRL + Qt.Key_3), self) self.uncommentScut.setContext(Qt.WidgetShortcut) self.uncommentScut.activated.connect(self.parent.pc.uncommentCode) def settingsEditor(self): # Set Python lexer self.setLexers() threshold = self.settings.value("pythonConsole/autoCompThresholdEditor", 2).toInt()[0] radioButtonSource = self.settings.value("pythonConsole/autoCompleteSourceEditor", 'fromAPI').toString() autoCompEnabled = self.settings.value("pythonConsole/autoCompleteEnabledEditor", True).toBool() self.setAutoCompletionThreshold(threshold) if autoCompEnabled: if radioButtonSource == 'fromDoc': self.setAutoCompletionSource(self.AcsDocument) elif radioButtonSource == 'fromAPI': self.setAutoCompletionSource(self.AcsAPIs) elif radioButtonSource == 'fromDocAPI': self.setAutoCompletionSource(self.AcsAll) else: self.setAutoCompletionSource(self.AcsNone) def autoCompleteKeyBinding(self): radioButtonSource = self.settings.value("pythonConsole/autoCompleteSourceEditor").toString() autoCompEnabled = self.settings.value("pythonConsole/autoCompleteEnabledEditor").toBool() if autoCompEnabled: if radioButtonSource == 'fromDoc': self.autoCompleteFromDocument() elif radioButtonSource == 'fromAPI': self.autoCompleteFromAPIs() elif radioButtonSource == 'fromDocAPI': self.autoCompleteFromAll() def on_margin_clicked(self, nmargin, nline, modifiers): # Toggle marker for the line the margin was clicked on if self.markersAtLine(nline) != 0: self.markerDelete(nline, self.ARROW_MARKER_NUM) else: self.markerAdd(nline, self.ARROW_MARKER_NUM) def setLexers(self): from qgis.core import QgsApplication self.lexer = QsciLexerPython() self.lexer.setIndentationWarning(QsciLexerPython.Inconsistent) self.lexer.setFoldComments(True) self.lexer.setFoldQuotes(True) loadFont = self.settings.value("pythonConsole/fontfamilytextEditor", "Monospace").toString() fontSize = self.settings.value("pythonConsole/fontsizeEditor", 10).toInt()[0] font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) font.setStyleHint(QFont.TypeWriter) font.setStretch(QFont.SemiCondensed) font.setLetterSpacing(QFont.PercentageSpacing, 87.0) font.setBold(False) self.lexer.setDefaultFont(font) self.lexer.setColor(Qt.red, 1) self.lexer.setColor(Qt.darkGreen, 5) self.lexer.setColor(Qt.darkBlue, 15) self.lexer.setFont(font, 1) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.api = QsciAPIs(self.lexer) chekBoxAPI = self.settings.value("pythonConsole/preloadAPI", True).toBool() if chekBoxAPI: self.api.loadPrepared( QgsApplication.pkgDataPath() + "/python/qsci_apis/pyqgis_master.pap" ) else: apiPath = self.settings.value("pythonConsole/userAPI").toStringList() for i in range(0, len(apiPath)): self.api.load(QString(unicode(apiPath[i]))) self.api.prepare() self.lexer.setAPIs(self.api) self.setLexer(self.lexer) def move_cursor_to_end(self): """Move cursor to end of text""" line, index = self.get_end_pos() self.setCursorPosition(line, index) self.ensureCursorVisible() self.ensureLineVisible(line) def get_end_pos(self): """Return (line, index) position of the last character""" line = self.lines() - 1 return (line, self.text(line).length()) def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/iconRunConsole.png") iconCodePad = QgsApplication.getThemeIcon("console/iconCodepadConsole.png") iconNewEditor = QgsApplication.getThemeIcon("console/iconTabEditorConsole.png") iconCommentEditor = QgsApplication.getThemeIcon("console/iconCommentEditorConsole.png") iconUncommentEditor = QgsApplication.getThemeIcon("console/iconUncommentEditorConsole.png") iconSettings = QgsApplication.getThemeIcon("console/iconSettingsConsole.png") hideEditorAction = menu.addAction("Hide Editor", self.hideEditor) menu.addSeparator() newTabAction = menu.addAction(iconNewEditor, "New Tab", self.parent.newTab, 'Ctrl+T') closeTabAction = menu.addAction("Close Tab", self.parent.close, 'Ctrl+W') menu.addSeparator() runSelected = menu.addAction(iconRun, "Enter selected", self.runSelectedCode, 'Ctrl+E') runScript = menu.addAction(iconRun, "Run Script", self.runScriptCode, 'Shift+Ctrl+E') menu.addSeparator() undoAction = menu.addAction("Undo", self.undo, QKeySequence.Undo) redoAction = menu.addAction("Redo", self.redo, QKeySequence.Redo) menu.addSeparator() cutAction = menu.addAction("Cut", self.cut, QKeySequence.Cut) copyAction = menu.addAction("Copy", self.copy, QKeySequence.Copy) pasteAction = menu.addAction("Paste", self.paste, QKeySequence.Paste) menu.addSeparator() commentCodeAction = menu.addAction(iconCommentEditor, "Comment", self.parent.pc.commentCode, 'Ctrl+3') uncommentCodeAction = menu.addAction(iconUncommentEditor, "Uncomment", self.parent.pc.uncommentCode, 'Shift+Ctrl+3') menu.addSeparator() codePadAction = menu.addAction(iconCodePad, "Share on codepad", self.codepad) menu.addSeparator() showCodeInspection = menu.addAction("Hide/Show Object list", self.objectListEditor) menu.addSeparator() selectAllAction = menu.addAction("Select All", self.selectAll, QKeySequence.SelectAll) menu.addSeparator() settingsDialog = menu.addAction(iconSettings, "Settings", self.parent.pc.openSettings) pasteAction.setEnabled(False) codePadAction.setEnabled(False) cutAction.setEnabled(False) runSelected.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) closeTabAction.setEnabled(False) undoAction.setEnabled(False) redoAction.setEnabled(False) if self.parent.mw.count() > 1: closeTabAction.setEnabled(True) if self.hasSelectedText(): runSelected.setEnabled(True) copyAction.setEnabled(True) cutAction.setEnabled(True) codePadAction.setEnabled(True) if not self.text() == '': selectAllAction.setEnabled(True) if self.isUndoAvailable(): undoAction.setEnabled(True) if self.isRedoAvailable(): redoAction.setEnabled(True) if QApplication.clipboard().text() != "": pasteAction.setEnabled(True) action = menu.exec_(self.mapToGlobal(e.pos())) def objectListEditor(self): listObj = self.parent.pc.listClassMethod if listObj.isVisible(): listObj.hide() self.parent.pc.objectListButton.setChecked(False) else: listObj.show() self.parent.pc.objectListButton.setChecked(True) def codepad(self): import urllib2, urllib listText = self.selectedText().split('\n') getCmd = [] for strLine in listText: if strLine != "": #if s[0:3] in (">>>", "..."): # filter for special command (_save,_clear) and comment if strLine[4] != "_" and strLine[:2] != "##": strLine.replace(">>> ", "").replace("... ", "") getCmd.append(unicode(strLine)) pasteText= u"\n".join(getCmd) url = 'http://codepad.org' values = {'lang' : 'Python', 'code' : pasteText, 'submit':'Submit'} try: response = urllib2.urlopen(url, urllib.urlencode(values)) url = response.read() for href in url.split("</a>"): if "Link:" in href: ind=href.index('Link:') found = href[ind+5:] for i in found.split('">'): if '<a href=' in i: link = i.replace('<a href="',"").strip() if link: QApplication.clipboard().setText(link) msgText = QCoreApplication.translate('PythonConsole', 'URL copied to clipboard.') self.parent.pc.callWidgetMessageBarEditor(msgText) except urllib2.URLError, e: msgText = QCoreApplication.translate('PythonConsole', 'Connection error: ') self.parent.pc.callWidgetMessageBarEditor(msgText + str(e.args))
class ShellScintilla(QsciScintilla, code.InteractiveInterpreter): def __init__(self, parent=None): super(ShellScintilla, self).__init__(parent) code.InteractiveInterpreter.__init__(self, locals=None) self.parent = parent self.settings = QSettings() # Enable non-ascii chars for editor self.setUtf8(True) self.new_input_line = True self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) self.buffer = [] self.displayPrompt(False) for line in _init_commands: self.runsource(line) self.history = QStringList() self.historyIndex = 0 # Read history command file self.readHistoryFile() # Brace matching: enable for a brace immediately before or after # the current position self.setBraceMatching(QsciScintilla.SloppyBraceMatch) # Current line visible with special background color self.setCaretWidth(2) self.settingsShell() # Don't want to see the horizontal scrollbar at all # Use raw message to Scintilla here (all messages are documented # here: http://www.scintilla.org/ScintillaDoc.html) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) # not too small # self.setMinimumSize(500, 300) self.setMinimumHeight(20) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER) ## Disable command key ctrl, shift = self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16 self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("L") + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("T") + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("D") + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Z") + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Y") + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("L") + ctrl + shift) ## New QShortcut = ctrl+space/ctrl+alt+space for Autocomplete self.newShortcutCSS = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Space), self) self.newShortcutCAS = QShortcut(QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_Space), self) self.newShortcutCSS.setContext(Qt.WidgetShortcut) self.newShortcutCAS.setContext(Qt.WidgetShortcut) self.newShortcutCAS.activated.connect(self.autoCompleteKeyBinding) self.newShortcutCSS.activated.connect(self.showHistory) self.connect(self, SIGNAL("userListActivated(int, const QString)"), self.completion_list_selected) def settingsShell(self): # Set Python lexer self.setLexers() threshold = self.settings.value("pythonConsole/autoCompThreshold", 2).toInt()[0] self.setAutoCompletionThreshold(threshold) radioButtonSource = self.settings.value("pythonConsole/autoCompleteSource", "fromAPI").toString() autoCompEnabled = self.settings.value("pythonConsole/autoCompleteEnabled", True).toBool() self.setAutoCompletionThreshold(threshold) if autoCompEnabled: if radioButtonSource == "fromDoc": self.setAutoCompletionSource(self.AcsDocument) elif radioButtonSource == "fromAPI": self.setAutoCompletionSource(self.AcsAPIs) elif radioButtonSource == "fromDocAPI": self.setAutoCompletionSource(self.AcsAll) else: self.setAutoCompletionSource(self.AcsNone) def showHistory(self): self.showUserList(1, QStringList(self.history)) def autoCompleteKeyBinding(self): radioButtonSource = self.settings.value("pythonConsole/autoCompleteSource").toString() autoCompEnabled = self.settings.value("pythonConsole/autoCompleteEnabled").toBool() if autoCompEnabled: if radioButtonSource == "fromDoc": self.autoCompleteFromDocument() elif radioButtonSource == "fromAPI": self.autoCompleteFromAPIs() elif radioButtonSource == "fromDocAPI": self.autoCompleteFromAll() def commandConsole(self, command): if not self.is_cursor_on_last_line(): self.move_cursor_to_end() line, pos = self.getCursorPosition() selCmdLenght = self.text(line).length() self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() if command == "sextante": # import Sextante class self.append("import sextante") elif command == "qtCore": # import QtCore class self.append("from PyQt4.QtCore import *") elif command == "qtGui": # import QtGui class self.append("from PyQt4.QtGui import *") self.entered() self.move_cursor_to_end() self.setFocus() def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace").toString() fontSize = self.settings.value("pythonConsole/fontsize", 10).toInt()[0] font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) font.setStyleHint(QFont.TypeWriter) font.setStretch(QFont.SemiCondensed) font.setLetterSpacing(QFont.PercentageSpacing, 87.0) font.setBold(False) self.lexer.setDefaultFont(font) self.lexer.setColor(Qt.red, 1) self.lexer.setColor(Qt.darkGreen, 5) self.lexer.setColor(Qt.darkBlue, 15) self.lexer.setFont(font, 1) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.api = QsciAPIs(self.lexer) chekBoxAPI = self.settings.value("pythonConsole/preloadAPI", True).toBool() if chekBoxAPI: self.api.loadPrepared(QgsApplication.pkgDataPath() + "/python/qsci_apis/pyqgis_master.pap") else: apiPath = self.settings.value("pythonConsole/userAPI").toStringList() for i in range(0, len(apiPath)): self.api.load(QString(unicode(apiPath[i]))) self.api.prepare() self.lexer.setAPIs(self.api) self.setLexer(self.lexer) ## TODO: show completion list for file and directory def completion_list_selected(self, id, txt): if id == 1: txt = unicode(txt) # get current cursor position line, pos = self.getCursorPosition() selCmdLength = self.text(line).length() # select typed text self.setSelection(line, 4, line, selCmdLength) self.removeSelectedText() self.insert(txt) def getText(self): """ Get the text as a unicode string. """ value = self.getBytes().decode("utf-8") # print (value) printing can give an error because the console font # may not have all unicode characters return value def getBytes(self): """ Get the text as bytes (utf-8 encoded). This is how the data is stored internally. """ len = self.SendScintilla(self.SCI_GETLENGTH) + 1 bb = QByteArray(len, "0") N = self.SendScintilla(self.SCI_GETTEXT, len, bb) return bytes(bb)[:-1] def getTextLength(self): return self.SendScintilla(QsciScintilla.SCI_GETLENGTH) def get_end_pos(self): """Return (line, index) position of the last character""" line = self.lines() - 1 return (line, self.text(line).length()) def is_cursor_at_end(self): """Return True if cursor is at the end of text""" cline, cindex = self.getCursorPosition() return (cline, cindex) == self.get_end_pos() def move_cursor_to_end(self): """Move cursor to end of text""" line, index = self.get_end_pos() self.setCursorPosition(line, index) self.ensureCursorVisible() self.ensureLineVisible(line) # def on_new_line(self): # """On new input line""" # self.move_cursor_to_end() # self.new_input_line = False def is_cursor_on_last_line(self): """Return True if cursor is on the last line""" cline, _ = self.getCursorPosition() return cline == self.lines() - 1 def is_cursor_on_edition_zone(self): """ Return True if the cursor is in the edition zone """ cline, cindex = self.getCursorPosition() return cline == self.lines() - 1 and cindex >= 4 def new_prompt(self, prompt): """ Print a new prompt and save its (line, index) position """ self.write(prompt, prompt=True) # now we update our cursor giving end of prompt line, index = self.getCursorPosition() self.ensureCursorVisible() self.ensureLineVisible(line) def displayPrompt(self, more=False): self.append("... ") if more else self.append(">>> ") self.move_cursor_to_end() def updateHistory(self, command): if isinstance(command, QStringList): for line in command: self.history.append(line) elif not command == "": if len(self.history) <= 0 or not command == self.history[-1]: self.history.append(command) self.historyIndex = len(self.history) def writeHistoryFile(self): wH = open(_historyFile, "w") for s in self.history: wH.write(s + "\n") wH.close() def readHistoryFile(self): fileExist = QFile.exists(_historyFile) if fileExist: rH = open(_historyFile, "r") for line in rH: if line != "\n": l = line.rstrip("\n") self.updateHistory(l) else: return def clearHistoryFile(self): cH = open(_historyFile, "w") cH.close() def showPrevious(self): if self.historyIndex < len(self.history) and not self.history.isEmpty(): line, pos = self.getCursorPosition() selCmdLenght = self.text(line).length() self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() self.historyIndex += 1 if self.historyIndex == len(self.history): self.insert("") pass else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() # self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def showNext(self): if self.historyIndex > 0 and not self.history.isEmpty(): line, pos = self.getCursorPosition() selCmdLenght = self.text(line).length() self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() self.historyIndex -= 1 if self.historyIndex == len(self.history): self.insert("") else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() # self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def keyPressEvent(self, e): startLine, _, endLine, _ = self.getSelection() # handle invalid cursor position and multiline selections if not self.is_cursor_on_edition_zone() or startLine < endLine: # allow to copy and select if e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier): if e.key() in (Qt.Key_C, Qt.Key_A): QsciScintilla.keyPressEvent(self, e) return # allow selection if e.modifiers() & Qt.ShiftModifier: if e.key() in (Qt.Key_Left, Qt.Key_Right, Qt.Key_Home, Qt.Key_End): QsciScintilla.keyPressEvent(self, e) return # all other keystrokes get sent to the input line self.move_cursor_to_end() line, index = self.getCursorPosition() cmd = self.text(line) if e.key() in (Qt.Key_Return, Qt.Key_Enter) and not self.isListActive(): self.entered() elif e.key() in (Qt.Key_Left, Qt.Key_Home): QsciScintilla.keyPressEvent(self, e) # check whether the cursor is moved out of the edition zone newline, newindex = self.getCursorPosition() if newline < line or newindex < 4: # fix selection and the cursor position if self.hasSelectedText(): self.setSelection(line, self.getSelection()[3], line, 4) else: self.setCursorPosition(line, 4) elif e.key() in (Qt.Key_Backspace, Qt.Key_Delete): QsciScintilla.keyPressEvent(self, e) # check whether the cursor is moved out of the edition zone _, newindex = self.getCursorPosition() if newindex < 4: # restore the prompt chars (if removed) and # fix the cursor position self.insert(cmd[: 3 - newindex] + " ") self.setCursorPosition(line, 4) self.recolor() elif e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier) and e.key() == Qt.Key_V: self.paste() e.accept() elif e.key() == Qt.Key_Down and not self.isListActive(): self.showPrevious() elif e.key() == Qt.Key_Up and not self.isListActive(): self.showNext() ## TODO: press event for auto-completion file directory else: QsciScintilla.keyPressEvent(self, e) def contextMenuEvent(self, e): menu = QMenu(self) copyAction = menu.addAction("Copy", self.copy, QKeySequence.Copy) pasteAction = menu.addAction("Paste", self.paste, QKeySequence.Paste) copyAction.setEnabled(False) pasteAction.setEnabled(False) if self.hasSelectedText(): copyAction.setEnabled(True) if QApplication.clipboard().text() != "": pasteAction.setEnabled(True) action = menu.exec_(self.mapToGlobal(e.pos())) def mousePressEvent(self, e): """ Re-implemented to handle the mouse press event. e: the mouse press event (QMouseEvent) """ self.setFocus() if e.button() == Qt.MidButton: stringSel = unicode(QApplication.clipboard().text(QClipboard.Selection)) if not self.is_cursor_on_last_line(): self.move_cursor_to_end() self.insertFromDropPaste(stringSel) e.accept() else: QsciScintilla.mousePressEvent(self, e) def paste(self): """ Method to display data from the clipboard. XXX: It should reimplement the virtual QScintilla.paste method, but it seems not used by QScintilla code. """ stringPaste = unicode(QApplication.clipboard().text()) if self.is_cursor_on_last_line(): if self.hasSelectedText(): self.removeSelectedText() else: self.move_cursor_to_end() self.insertFromDropPaste(stringPaste) ## Drag and drop def dropEvent(self, e): if e.mimeData().hasText(): stringDrag = e.mimeData().text() self.insertFromDropPaste(stringDrag) self.setFocus() e.setDropAction(Qt.CopyAction) e.accept() else: QsciScintillaCompat.dropEvent(self, e) def insertFromDropPaste(self, textDP): pasteList = str(textDP).splitlines() for line in pasteList[:-1]: line.replace(">>> ", "").replace("... ", "") self.insert(unicode(line)) self.move_cursor_to_end() self.runCommand(unicode(self.currentCommand())) if pasteList[-1] != "": line = pasteList[-1] line.replace(">>> ", "").replace("... ", "") self.insert(unicode(line)) self.move_cursor_to_end() def insertTextFromFile(self, listOpenFile): for line in listOpenFile[:-1]: self.append(line) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) self.runCommand(unicode(self.currentCommand())) self.append(unicode(listOpenFile[-1])) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def entered(self): self.move_cursor_to_end() self.runCommand(unicode(self.currentCommand())) self.setFocus() self.move_cursor_to_end() # self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER) def currentCommand(self): linenr, index = self.getCursorPosition() # for i in range(0, linenr): txtLength = self.text(linenr).length() string = self.text() cmdLine = string.right(txtLength - 4) cmd = unicode(cmdLine) return cmd def runCommand(self, cmd): self.writeCMD(cmd) import webbrowser self.updateHistory(cmd) line, pos = self.getCursorPosition() selCmdLenght = self.text(line).length() self.setSelection(line, 0, line, selCmdLenght) self.removeSelectedText() if cmd in ("_save", "_clear", "_clearAll", "_pyqgis", "_api"): if cmd == "_save": self.writeHistoryFile() msgText = QCoreApplication.translate("PythonConsole", "History saved successfully.") elif cmd == "_clear": self.clearHistoryFile() msgText = QCoreApplication.translate("PythonConsole", "History cleared successfully.") elif cmd == "_clearAll": self.history = QStringList() self.clearHistoryFile() msgText = QCoreApplication.translate("PythonConsole", "Session and file history cleared successfully.") elif cmd == "_pyqgis": webbrowser.open("http://www.qgis.org/pyqgis-cookbook/") elif cmd == "_api": webbrowser.open("http://www.qgis.org/api/") if msgText: self.parent.callWidgetMessageBar(msgText) self.displayPrompt(False) else: self.buffer.append(cmd) src = u"\n".join(self.buffer) more = self.runsource(src, "<input>") if not more: self.buffer = [] ## prevents to commands with more lines to break the console ## in the case they have a eol different from '\n' self.setText("") self.move_cursor_to_end() self.displayPrompt(more) def write(self, txt): sys.stderr.write(txt) def writeCMD(self, txt): if len(txt) > 0: getCmdString = self.text() prompt = getCmdString[0:4] sys.stdout.write(prompt + txt + "\n")
class PythonEdit(QsciScintilla, code.InteractiveInterpreter): def __init__(self, parent=None): #QsciScintilla.__init__(self, parent) super(PythonEdit,self).__init__(parent) code.InteractiveInterpreter.__init__(self, locals=None) # Enable non-ascii chars for editor self.setUtf8(True) self.new_input_line = True self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) self.buffer = [] self.insertInitText() self.displayPrompt(False) for line in _init_commands: self.runsource(line) self.history = QStringList() self.historyIndex = 0 # Read history command file self.readHistoryFile() # Brace matching: enable for a brace immediately before or after # the current position self.setBraceMatching(QsciScintilla.SloppyBraceMatch) #self.moveToMatchingBrace() #self.selectToMatchingBrace() # Current line visible with special background color self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor("#ffe4e4")) self.setCaretWidth(2) # Set Python lexer # Set style for Python comments (style number 1) to a fixed-width # courier. self.setLexers() # Indentation #self.setAutoIndent(True) #self.setIndentationsUseTabs(False) #self.setIndentationWidth(4) #self.setTabIndents(True) #self.setBackspaceUnindents(True) #self.setTabWidth(4) self.setAutoCompletionThreshold(2) self.setAutoCompletionSource(self.AcsAPIs) # Don't want to see the horizontal scrollbar at all # Use raw message to Scintilla here (all messages are documented # here: http://www.scintilla.org/ScintillaDoc.html) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) # not too small #self.setMinimumSize(500, 300) self.setMinimumHeight(125) self.SendScintilla(QsciScintilla.SCI_SETWRAPMODE, 1) self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER) ## Disable command key ctrl, shift = self.SCMOD_CTRL<<16, self.SCMOD_SHIFT<<16 self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Z')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Y')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L')+ ctrl+shift) ## New QShortcut = ctrl+space/ctrl+alt+space for Autocomplete self.newShortcutCS = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Space), self) self.newShortcutCAS = QShortcut(QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_Space), self) self.newShortcutCS.activated.connect(self.autoComplete) self.newShortcutCAS.activated.connect(self.showHistory) self.connect(self, SIGNAL('userListActivated(int, const QString)'), self.completion_list_selected) def showHistory(self): self.showUserList(1, QStringList(self.history)) def autoComplete(self): self.autoCompleteFromAll() def clearConsole(self): """Clear the contents of the console.""" self.SendScintilla(QsciScintilla.SCI_CLEARALL) #self.setText('') self.insertInitText() self.displayPrompt(False) self.setFocus() def commandConsole(self, command): if not self.is_cursor_on_last_line(): self.move_cursor_to_end() line, pos = self.getCursorPosition() selCmdLenght = self.text(line).length() self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() if command == "iface": """Import QgisInterface class""" self.append('from qgis.utils import iface') self.move_cursor_to_end() elif command == "sextante": """Import Sextante class""" self.append('from sextante.core.Sextante import Sextante') self.move_cursor_to_end() elif command == "cLayer": """Retrieve current Layer from map camvas""" self.append('cLayer = iface.mapCanvas().currentLayer()') self.move_cursor_to_end() elif command == "qtCore": """Import QtCore class""" self.append('from PyQt4.QtCore import *') self.move_cursor_to_end() elif command == "qtGui": """Import QtGui class""" self.append('from PyQt4.QtGui import *') self.move_cursor_to_end() self.setFocus() def setLexers(self): from qgis.core import QgsApplication self.lexer = QsciLexerPython() settings = QSettings() loadFont = settings.value("pythonConsole/fontfamilytext", "Monospace").toString() fontSize = settings.value("pythonConsole/fontsize", 10).toInt()[0] font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) self.lexer.setDefaultFont(font) self.lexer.setColor(Qt.red, 1) self.lexer.setColor(Qt.darkGreen, 5) self.lexer.setColor(Qt.darkBlue, 15) self.lexer.setFont(font, 1) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.api = QsciAPIs(self.lexer) chekBoxAPI = settings.value( "pythonConsole/preloadAPI" ).toBool() if chekBoxAPI: self.api.loadPrepared( QgsApplication.pkgDataPath() + "/python/qsci_apis/pyqgis_master.pap" ) else: apiPath = settings.value("pythonConsole/userAPI").toStringList() for i in range(0, len(apiPath)): self.api.load(QString(unicode(apiPath[i]))) self.api.prepare() self.lexer.setAPIs(self.api) self.setLexer(self.lexer) ## TODO: show completion list for file and directory def completion_list_selected(self, id, txt): if id == 1: txt = unicode(txt) # get current cursor position line, pos = self.getCursorPosition() selCmdLength = self.text(line).length() # select typed text self.setSelection(line, 4, line, selCmdLength) self.removeSelectedText() self.insert(txt) def insertInitText(self): #self.setLexers(False) txtInit = QCoreApplication.translate("PythonConsole", "## To access Quantum GIS environment from this console\n" "## use qgis.utils.iface object (instance of QgisInterface class). Read help for more info.\n\n") initText = self.setText(txtInit) def getText(self): """ Get the text as a unicode string. """ value = self.getBytes().decode('utf-8') # print (value) printing can give an error because the console font # may not have all unicode characters return value def getBytes(self): """ Get the text as bytes (utf-8 encoded). This is how the data is stored internally. """ len = self.SendScintilla(self.SCI_GETLENGTH)+1 bb = QByteArray(len,'0') N = self.SendScintilla(self.SCI_GETTEXT, len, bb) return bytes(bb)[:-1] def getTextLength(self): return self.SendScintilla(QsciScintilla.SCI_GETLENGTH) def get_end_pos(self): """Return (line, index) position of the last character""" line = self.lines() - 1 return (line, self.text(line).length()) def is_cursor_at_end(self): """Return True if cursor is at the end of text""" cline, cindex = self.getCursorPosition() return (cline, cindex) == self.get_end_pos() def move_cursor_to_end(self): """Move cursor to end of text""" line, index = self.get_end_pos() self.setCursorPosition(line, index) self.ensureCursorVisible() self.ensureLineVisible(line) # def on_new_line(self): # """On new input line""" # self.move_cursor_to_end() # self.new_input_line = False def is_cursor_on_last_line(self): """Return True if cursor is on the last line""" cline, _ = self.getCursorPosition() return cline == self.lines() - 1 def is_cursor_on_edition_zone(self): """ Return True if the cursor is in the edition zone """ cline, cindex = self.getCursorPosition() return cline == self.lines() - 1 and cindex >= 4 def new_prompt(self, prompt): """ Print a new prompt and save its (line, index) position """ self.write(prompt, prompt=True) # now we update our cursor giving end of prompt line, index = self.getCursorPosition() self.ensureCursorVisible() self.ensureLineVisible(line) def refreshLexerProperties(self): self.setLexers() # def check_selection(self): # """ # Check if selected text is r/w, # otherwise remove read-only parts of selection # """ # #if self.current_prompt_pos is None: # #self.move_cursor_to_end() # #return # line_from, index_from, line_to, index_to = self.getSelection() # pline, pindex = self.getCursorPosition() # if line_from < pline or \ # (line_from == pline and index_from < pindex): # self.setSelection(pline, pindex, line_to, index_to) def displayPrompt(self, more=False): self.append("... ") if more else self.append(">>> ") self.move_cursor_to_end() def updateHistory(self, command): if isinstance(command, QStringList): for line in command: self.history.append(line) elif not command == "": if len(self.history) <= 0 or \ not command == self.history[-1]: self.history.append(command) self.historyIndex = len(self.history) def writeHistoryFile(self): wH = open(_historyFile, 'w') for s in self.history: wH.write(s + '\n') wH.close() def readHistoryFile(self): fileExist = QFile.exists(_historyFile) if fileExist: rH = open(_historyFile, 'r') for line in rH: if line != "\n": l = line.rstrip('\n') self.updateHistory(l) else: return def clearHistoryFile(self): cH = open(_historyFile, 'w') cH.close() def showPrevious(self): if self.historyIndex < len(self.history) and not self.history.isEmpty(): line, pos = self.getCursorPosition() selCmdLenght = self.text(line).length() self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() self.historyIndex += 1 if self.historyIndex == len(self.history): self.insert("") pass else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def showNext(self): if self.historyIndex > 0 and not self.history.isEmpty(): line, pos = self.getCursorPosition() selCmdLenght = self.text(line).length() self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() self.historyIndex -= 1 if self.historyIndex == len(self.history): self.insert("") else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def keyPressEvent(self, e): startLine, _, endLine, _ = self.getSelection() # handle invalid cursor position and multiline selections if not self.is_cursor_on_edition_zone() or startLine < endLine: # allow to copy and select if e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier): if e.key() in (Qt.Key_C, Qt.Key_A): QsciScintilla.keyPressEvent(self, e) return # allow selection if e.modifiers() & Qt.ShiftModifier: if e.key() in (Qt.Key_Left, Qt.Key_Right, Qt.Key_Home, Qt.Key_End): QsciScintilla.keyPressEvent(self, e) return # all other keystrokes get sent to the input line self.move_cursor_to_end() line, index = self.getCursorPosition() cmd = self.text(line) if e.key() in (Qt.Key_Return, Qt.Key_Enter) and not self.isListActive(): self.entered() elif e.key() in (Qt.Key_Left, Qt.Key_Home): QsciScintilla.keyPressEvent(self, e) # check whether the cursor is moved out of the edition zone newline, newindex = self.getCursorPosition() if newline < line or newindex < 4: # fix selection and the cursor position if self.hasSelectedText(): self.setSelection(line, self.getSelection()[3], line, 4) else: self.setCursorPosition(line, 4) elif e.key() in (Qt.Key_Backspace, Qt.Key_Delete): QsciScintilla.keyPressEvent(self, e) # check whether the cursor is moved out of the edition zone _, newindex = self.getCursorPosition() if newindex < 4: # restore the prompt chars (if removed) and # fix the cursor position self.insert( cmd[:3-newindex] + " " ) self.setCursorPosition(line, 4) elif e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier) and \ e.key() == Qt.Key_V: self.paste() e.accept() elif e.key() == Qt.Key_Down and not self.isListActive(): self.showPrevious() elif e.key() == Qt.Key_Up and not self.isListActive(): self.showNext() ## TODO: press event for auto-completion file directory else: QsciScintilla.keyPressEvent(self, e) def mousePressEvent(self, e): """ Re-implemented to handle the mouse press event. e: the mouse press event (QMouseEvent) """ self.setFocus() if e.button() == Qt.MidButton: stringSel = unicode(QApplication.clipboard().text(QClipboard.Selection)) if not self.is_cursor_on_last_line(): self.move_cursor_to_end() self.insertFromDropPaste(stringSel) e.accept() else: QsciScintilla.mousePressEvent(self, e) def paste(self): """ Method to display data from the clipboard. XXX: It should reimplement the virtual QScintilla.paste method, but it seems not used by QScintilla code. """ stringPaste = unicode(QApplication.clipboard().text()) if self.is_cursor_on_last_line(): if self.hasSelectedText(): self.removeSelectedText() else: self.move_cursor_to_end() self.insertFromDropPaste(stringPaste) ## Drag and drop def dropEvent(self, e): if e.mimeData().hasText(): stringDrag = e.mimeData().text() self.insertFromDropPaste(stringDrag) self.setFocus() e.setDropAction(Qt.MoveAction) e.accept() else: QsciScintillaCompat.dropEvent(self, e) def insertFromDropPaste(self, textDP): pasteList = textDP.split("\n") for line in pasteList[:-1]: self.insert(line) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) self.runCommand(unicode(self.currentCommand())) if pasteList[-1] != "": self.insert(unicode(pasteList[-1])) self.move_cursor_to_end() def getTextFromEditor(self): text = self.text() textList = text.split("\n") return textList def insertTextFromFile(self, listOpenFile): for line in listOpenFile[:-1]: self.append(line) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) self.runCommand(unicode(self.currentCommand())) self.append(unicode(listOpenFile[-1])) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def entered(self): self.move_cursor_to_end() self.runCommand( unicode(self.currentCommand()) ) self.setFocus() self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER) def currentCommand(self): linenr, index = self.getCursorPosition() #for i in range(0, linenr): txtLength = self.text(linenr).length() string = self.text() cmdLine = string.right(txtLength - 4) cmd = unicode(cmdLine) return cmd def runCommand(self, cmd): import webbrowser self.updateHistory(cmd) self.SendScintilla(QsciScintilla.SCI_NEWLINE) if cmd in ('_save', '_clear', '_clearAll', '_pyqgis', '_api'): if cmd == '_save': self.writeHistoryFile() print QCoreApplication.translate("PythonConsole", "## History saved successfully ##") elif cmd == '_clear': self.clearHistoryFile() print QCoreApplication.translate("PythonConsole", "## History cleared successfully ##") elif cmd == '_clearAll': res = QMessageBox.question(self, "Python Console", QCoreApplication.translate("PythonConsole", "Are you sure you want to completely\n" "delete the command history ?"), QMessageBox.Yes | QMessageBox.No) if res == QMessageBox.No: self.SendScintilla(QsciScintilla.SCI_DELETEBACK) return self.history = QStringList() self.clearHistoryFile() print QCoreApplication.translate("PythonConsole", "## History cleared successfully ##") elif cmd == '_pyqgis': webbrowser.open( "http://www.qgis.org/pyqgis-cookbook/" ) elif cmd == '_api': webbrowser.open( "http://www.qgis.org/api/" ) output = sys.stdout.get_and_clean_data() if output: self.append(output) self.displayPrompt(False) else: self.buffer.append(cmd) src = u"\n".join(self.buffer) more = self.runsource(src, "<input>") if not more: self.buffer = [] output = sys.stdout.get_and_clean_data() if output: self.append(output) self.move_cursor_to_end() self.displayPrompt(more) def write(self, txt): self.SendScintilla(QsciScintilla.SCI_SETSTYLING, len(txt), 1) self.append(txt) self.SendScintilla(QsciScintilla.SCI_SETSTYLING, len(txt), 1)
class ShellOutputScintilla(QsciScintilla): def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretWidth(0) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemeted copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate( "PythonConsole", "Python Console \n" "Use iface to access QGIS API interface or Type help(iface) for more info" ) ## some translation string for the console header ends without '\n' ## and the first command in console will be appended at the header text. ## The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def refreshSettingsOutput(self): # Set Python lexer self.setLexers() caretLineColor = self.settings.value("pythonConsole/caretLineColor", QColor("#fcf3ed")) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(Qt.black)) self.setCaretLineBackgroundColor(caretLineColor) self.setCaretForegroundColor(cursorColor) def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace") fontSize = self.settings.value("pythonConsole/fontsize", 10, type=int) font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) font.setStyleHint(QFont.TypeWriter) font.setStretch(QFont.SemiCondensed) font.setLetterSpacing(QFont.PercentageSpacing, 87.0) font.setBold(False) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor( QColor( self.settings.value("pythonConsole/defaultFontColor", QColor(Qt.black)))) self.lexer.setColor( QColor( self.settings.value("pythonConsole/commentFontColor", QColor(Qt.gray))), 1) self.lexer.setColor( QColor( self.settings.value("pythonConsole/keywordFontColor", QColor(Qt.darkGreen))), 5) self.lexer.setColor( QColor( self.settings.value("pythonConsole/classFontColor", QColor(Qt.blue))), 8) self.lexer.setColor( QColor( self.settings.value("pythonConsole/methodFontColor", QColor(Qt.darkGray))), 9) self.lexer.setColor( QColor( self.settings.value("pythonConsole/decorFontColor", QColor(Qt.darkBlue))), 15) self.lexer.setColor( QColor( self.settings.value("pythonConsole/commentBlockFontColor", QColor(Qt.gray))), 12) self.lexer.setColor( QColor( self.settings.value("pythonConsole/singleQuoteFontColor", QColor(Qt.blue))), 4) self.lexer.setColor( QColor( self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(Qt.blue))), 3) self.lexer.setColor( QColor( self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(Qt.blue))), 6) self.lexer.setColor( QColor( self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(Qt.blue))), 7) self.lexer.setColor(QColor(Qt.red), 14) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) for style in range(0, 33): paperColor = QColor( self.settings.value("pythonConsole/paperBackgroundColor", QColor(Qt.white))) self.lexer.setPaper(paperColor, style) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/iconRunConsole.png") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.png") iconHideTool = QgsApplication.getThemeIcon( "console/iconHideToolConsole.png") iconSettings = QgsApplication.getThemeIcon( "console/iconSettingsConsole.png") menu.addAction( iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction( iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction( iconClear, QCoreApplication.translate("PythonConsole", "Clear Console"), self.clearConsole) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction( iconSettings, QCoreApplication.translate("PythonConsole", "Options..."), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = unicode(self.selectedText()) text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: self.emit(SIGNAL("keyboard_interrupt()")) def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, QgsMessageBar.INFO, timeout)
class PythonEdit(QsciScintilla, code.InteractiveInterpreter): def __init__(self, parent=None): #QsciScintilla.__init__(self, parent) super(PythonEdit,self).__init__(parent) code.InteractiveInterpreter.__init__(self, locals=None) self.current_prompt_pos = None self.new_input_line = True self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) self.buffer = [] self.insertInitText() self.setCursorPosition(4,4) self.displayPrompt(False) for line in _init_commands: self.runsource(line) self.history = QStringList() self.historyIndex = 0 # Read history command file self.readHistoryFile() # Brace matching: enable for a brace immediately before or after # the current position self.setBraceMatching(QsciScintilla.SloppyBraceMatch) #self.moveToMatchingBrace() #self.selectToMatchingBrace() # Current line visible with special background color self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor("#ffe4e4")) self.setCaretWidth(2) # Set Python lexer # Set style for Python comments (style number 1) to a fixed-width # courier. self.setLexers(True) # Indentation #self.setAutoIndent(True) #self.setIndentationsUseTabs(False) #self.setIndentationWidth(4) #self.setTabIndents(True) #self.setBackspaceUnindents(True) #self.setTabWidth(4) self.setAutoCompletionThreshold(2) self.setAutoCompletionSource(self.AcsAPIs) # Don't want to see the horizontal scrollbar at all # Use raw message to Scintilla here (all messages are documented # here: http://www.scintilla.org/ScintillaDoc.html) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) # not too small #self.setMinimumSize(500, 300) self.setMinimumHeight(125) self.SendScintilla(QsciScintilla.SCI_SETWRAPMODE, 1) self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER) ## Disable command key ctrl, shift = self.SCMOD_CTRL<<16, self.SCMOD_SHIFT<<16 self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Z')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Y')+ ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L')+ ctrl+shift) ## New QShortcut = ctrl+space/ctrl+alt+space for Autocomplete self.newShortcutCS = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Space), self) self.newShortcutCAS = QShortcut(QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_Space), self) self.newShortcutCS.activated.connect(self.autoComplete) self.newShortcutCAS.activated.connect(self.showHistory) self.connect(self, SIGNAL('userListActivated(int, const QString)'), self.completion_list_selected) def showHistory(self): self.showUserList(1, QStringList(self.history)) def autoComplete(self): self.autoCompleteFromAll() def clearConsole(self): """Clear the contents of the console.""" self.setText('') self.insertInitText() self.displayPrompt(False) self.setFocus() def commandConsole(self, command): if not self.is_cursor_on_last_line(): self.move_cursor_to_end() line, pos = self.getCurLine() selCmd= self.text(line).length() self.setSelection(line, 4, line, selCmd) self.removeSelectedText() if command == "iface": """Import QgisInterface class""" self.append('from qgis.utils import iface') self.move_cursor_to_end() elif command == "sextante": """Import Sextante class""" self.append('from sextante.core.Sextante import Sextante') self.move_cursor_to_end() elif command == "cLayer": """Retrive current Layer from map camvas""" self.append('cLayer = iface.mapCanvas().currentLayer()') self.move_cursor_to_end() self.setFocus() def setLexers(self, lexer): if lexer: font = QFont() font.setFamily('Mono') ## Courier New font.setFixedPitch(True) ## check platform for font size if sys.platform.startswith('darwin'): font.setPointSize(13) else: font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) self.lexer = QsciLexerPython() self.lexer.setDefaultFont(font) self.lexer.setColor(Qt.red, 1) self.lexer.setColor(Qt.darkGreen, 5) self.lexer.setColor(Qt.darkBlue, 15) self.lexer.setFont(font, 1) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.api = QsciAPIs(self.lexer) self.api.loadPrepared(QString(os.path.dirname(__file__) + "/api/pyqgis_master.pap")) self.setLexer(self.lexer) ## TODO: show completion list for file and directory def completion_list_selected(self, id, txt): if id == 1: txt = unicode(txt) # get current cursor position line, pos = self.getCurLine() selCmd= self.text(line).length() # select typed text self.setSelection(line, 4, line, selCmd) self.removeSelectedText() self.insert(txt) def insertInitText(self): #self.setLexers(False) txtInit = QCoreApplication.translate("PythonConsole", "## To access Quantum GIS environment from this console\n" "## use qgis.utils.iface object (instance of QgisInterface class). Read help for more info.\n\n") initText = self.setText(txtInit) def getCurrentPos(self): """ Get the position (as an int) of the cursor. getCursorPosition() returns a (linenr, index) tuple. """ return self.SendScintilla(self.SCI_GETCURRENTPOS) def getText(self): """ Get the text as a unicode string. """ value = self.getBytes().decode('utf-8') # print (value) printing can give an error because the console font # may not have all unicode characters return value def getBytes(self): """ Get the text as bytes (utf-8 encoded). This is how the data is stored internally. """ len = self.SendScintilla(self.SCI_GETLENGTH)+1 bb = QByteArray(len,'0') N = self.SendScintilla(self.SCI_GETTEXT, len, bb) return bytes(bb)[:-1] def getTextLength(self): return self.SendScintilla(QsciScintilla.SCI_GETLENGTH) def getLine(self, linenr): """ Get the bytes on the given line number. """ len = self.SendScintilla(QsciScintilla.SCI_LINELENGTH)+1 bb = QByteArray(len,'0') N = self.SendScintilla(QsciScintilla.SCI_GETLINE, len, bb) return bytes(bb)[:-1] def getCurLine(self): """ Get the current line (as a string) and the position of the cursor in it. """ linenr, index = self.getCursorPosition() #line = self.getLine(linenr) #.decode('utf-8') return linenr, index def get_end_pos(self): """Return (line, index) position of the last character""" line = self.lines() - 1 return (line, self.text(line).length()) def is_cursor_at_end(self): """Return True if cursor is at the end of text""" cline, cindex = self.getCursorPosition() return (cline, cindex) == self.get_end_pos() def move_cursor_to_end(self): """Move cursor to end of text""" line, index = self.get_end_pos() self.setCursorPosition(line, index) self.ensureCursorVisible() def on_new_line(self): """On new input line""" self.move_cursor_to_end() self.current_prompt_pos = self.getCursorPosition() self.new_input_line = False def is_cursor_on_last_line(self): """Return True if cursor is on the last line""" cline, _ = self.getCursorPosition() return cline == self.lines() - 1 def new_prompt(self, prompt): """ Print a new prompt and save its (line, index) position """ self.write(prompt, prompt=True) # now we update our cursor giving end of prompt self.current_prompt_pos = self.getCursorPosition() self.ensureCursorVisible() def check_selection(self): """ Check if selected text is r/w, otherwise remove read-only parts of selection """ #if self.current_prompt_pos is None: #self.move_cursor_to_end() #return line_from, index_from, line_to, index_to = self.getSelection() pline, pindex = self.getCursorPosition() if line_from < pline or \ (line_from == pline and index_from < pindex): self.setSelection(pline, pindex, line_to, index_to) def displayPrompt(self, more=False): self.append("... ") if more else self.append(">>> ") self.move_cursor_to_end() def updateHistory(self, command): if isinstance(command, QStringList): for line in command: self.history.append(line) elif not command == "": if len(self.history) <= 0 or \ not command == self.history[-1]: self.history.append(command) self.historyIndex = len(self.history) def writeHistoryFile(self): wH = open(_historyFile, 'w') for s in self.history: wH.write(s + '\n') wH.close() def readHistoryFile(self): fileExist = QFile.exists(_historyFile) if fileExist: rH = open(_historyFile, 'r') for line in rH: if line != "\n": l = line.rstrip('\n') self.updateHistory(l) else: return def clearHistoryFile(self): cH = open(_historyFile, 'w') cH.close() def showPrevious(self): if self.historyIndex < len(self.history) and not self.history.isEmpty(): line, pos = self.getCurLine() selCmd= self.text(line).length() self.setSelection(line, 4, line, selCmd) self.removeSelectedText() self.historyIndex += 1 if self.historyIndex == len(self.history): self.insert("") pass else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def showNext(self): if self.historyIndex > 0 and not self.history.isEmpty(): line, pos = self.getCurLine() selCmd = self.text(line).length() self.setSelection(line, 4, line, selCmd) self.removeSelectedText() self.historyIndex -= 1 if self.historyIndex == len(self.history): self.insert("") else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def keyPressEvent(self, e): linenr, index = self.getCurLine() if not self.is_cursor_on_last_line() or index < 4: if e.modifiers() & Qt.ControlModifier or e.modifiers() & Qt.MetaModifier: if e.key() == Qt.Key_C or e.key() == Qt.Key_A: QsciScintilla.keyPressEvent(self, e) else: # all other keystrokes get sent to the input line self.move_cursor_to_end() #pass else: if (e.key() == Qt.Key_Return or e.key() == Qt.Key_Enter) and not self.isListActive(): self.entered() elif e.modifiers() & Qt.ControlModifier: if e.key() == Qt.Key_V: self.paste() elif e.key() == Qt.Key_C: self.copy() elif e.key() == Qt.Key_X: self.cut() elif e.key() == Qt.Key_Left: e.accept() if e.modifiers() & Qt.ShiftModifier: if index > 4: if e.modifiers() & Qt.ControlModifier: self.SendScintilla(QsciScintilla.SCI_WORDLEFTEXTEND) else: self.SendScintilla(QsciScintilla.SCI_CHARLEFTEXTEND) else: if index > 4: if e.modifiers() & Qt.ControlModifier: self.SendScintilla(QsciScintilla.SCI_WORDLEFT) else: self.SendScintilla(QsciScintilla.SCI_CHARLEFT) elif e.key() == Qt.Key_Right: e.accept() if e.modifiers() & Qt.ShiftModifier: if index >= 4: if e.modifiers() & Qt.ControlModifier: self.SendScintilla(QsciScintilla.SCI_WORDRIGHTEXTEND) else: self.SendScintilla(QsciScintilla.SCI_CHARRIGHTEXTEND) else: if index >= 4: if e.modifiers() & Qt.ControlModifier: self.SendScintilla(QsciScintilla.SCI_WORDRIGHT) else: self.SendScintilla(QsciScintilla.SCI_CHARRIGHT) elif e.key() == Qt.Key_Backspace: curPos, pos = self.getCursorPosition() line = self.lines() -1 if curPos < line -1 or pos < 5: return #else: #self.move_cursor_to_end() QsciScintilla.keyPressEvent(self, e) elif e.key() == Qt.Key_Delete: if self.hasSelectedText(): self.removeSelectedText() elif self.is_cursor_on_last_line(): self.SendScintilla(QsciScintilla.SCI_CLEAR) e.accept() elif e.key() == Qt.Key_Home: self.setCursorPosition(linenr,4) self.ensureCursorVisible() elif e.key() == Qt.Key_Down and not self.isListActive(): self.showPrevious() elif e.key() == Qt.Key_Up and not self.isListActive(): self.showNext() ## TODO: press event for auto-completion file directory else: QsciScintilla.keyPressEvent(self, e) def paste(self): """Reimplement QScintilla method""" stringPaste = unicode(QApplication.clipboard().text()) if self.hasSelectedText(): self.removeSelectedText() self.insertFromDropPaste(stringPaste) ## Drag and drop def dropEvent(self, e): if e.mimeData().hasText(): stringDrag = e.mimeData().text() self.insertFromDropPaste(stringDrag) e.setDropAction(Qt.MoveAction) e.accept() else: QsciScintillaCompat.dropEvent(self, e) def insertFromDropPaste(self, textDP): pasteList = QStringList() pasteList = textDP.split("\n") for line in pasteList[:-1]: self.insert(line) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) self.runCommand(unicode(self.currentCommand())) self.insert(unicode(pasteList[-1])) self.move_cursor_to_end() def getTextFromEditor(self): text = self.text() textList = QStringList() textList = text.split("\n") return textList def insertTextFromFile(self, listOpenFile): for line in listOpenFile[:-1]: self.append(line) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) self.runCommand(unicode(self.currentCommand())) self.append(unicode(listOpenFile[-1])) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def entered(self): self.move_cursor_to_end() self.runCommand( unicode(self.currentCommand()) ) self.setFocus() #self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER) def currentCommand(self): linenr, index = self.getCurLine() #for i in range(0, linenr): txtLength = self.text(linenr).length() string = self.text() cmdLine = string.right(txtLength - 4) cmd = str(cmdLine) return cmd def runCommand(self, cmd): self.updateHistory(cmd) self.SendScintilla(QsciScintilla.SCI_NEWLINE) if cmd in ('_save', '_clear', '_clearAll'): if cmd == '_save': self.writeHistoryFile() print QCoreApplication.translate("PythonConsole", "## History saved successfully ##") elif cmd == '_clear': self.clearHistoryFile() print QCoreApplication.translate("PythonConsole", "## History cleared successfully ##") elif cmd == '_clearAll': res = QMessageBox.question(self, "Python Console", QCoreApplication.translate("PythonConsole", "Are you sure you want to completely\n" "delete the command history ?"), QMessageBox.Yes | QMessageBox.No) if res == QMessageBox.No: self.SendScintilla(QsciScintilla.SCI_DELETEBACK) return self.history = QStringList() self.clearHistoryFile() print QCoreApplication.translate("PythonConsole", "## History cleared successfully ##") output = sys.stdout.get_and_clean_data() if output: self.append(output) self.displayPrompt(False) else: self.buffer.append(cmd) src = "\n".join(self.buffer) more = self.runsource(src, "<input>") if not more: self.buffer = [] output = sys.stdout.get_and_clean_data() if output: self.append(output) self.move_cursor_to_end() self.displayPrompt(more) def write(self, txt): self.SendScintilla(QsciScintilla.SCI_SETSTYLING, len(txt), 1) self.append(txt) self.SendScintilla(QsciScintilla.SCI_SETSTYLING, len(txt), 1)
class PythonEdit(QsciScintilla, code.InteractiveInterpreter): def __init__(self, parent=None): #QsciScintilla.__init__(self, parent) super(PythonEdit, self).__init__(parent) code.InteractiveInterpreter.__init__(self, locals=None) self.current_prompt_pos = None self.new_input_line = True self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) self.buffer = [] self.insertInitText() self.setCursorPosition(4, 4) self.displayPrompt(False) for line in _init_commands: self.runsource(line) self.history = QStringList() self.historyIndex = 0 # Read history command file self.readHistoryFile() # Brace matching: enable for a brace immediately before or after # the current position self.setBraceMatching(QsciScintilla.SloppyBraceMatch) #self.moveToMatchingBrace() #self.selectToMatchingBrace() # Current line visible with special background color self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor("#ffe4e4")) self.setCaretWidth(2) # Set Python lexer # Set style for Python comments (style number 1) to a fixed-width # courier. self.setLexers(True) # Indentation #self.setAutoIndent(True) #self.setIndentationsUseTabs(False) #self.setIndentationWidth(4) #self.setTabIndents(True) #self.setBackspaceUnindents(True) #self.setTabWidth(4) self.setAutoCompletionThreshold(2) self.setAutoCompletionSource(self.AcsAPIs) # Don't want to see the horizontal scrollbar at all # Use raw message to Scintilla here (all messages are documented # here: http://www.scintilla.org/ScintillaDoc.html) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) # not too small #self.setMinimumSize(500, 300) self.setMinimumHeight(125) self.SendScintilla(QsciScintilla.SCI_SETWRAPMODE, 1) self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER) ## Disable command key ctrl, shift = self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16 self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Z') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Y') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl + shift) ## New QShortcut = ctrl+space/ctrl+alt+space for Autocomplete self.newShortcutCS = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Space), self) self.newShortcutCAS = QShortcut( QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_Space), self) self.newShortcutCS.activated.connect(self.autoComplete) self.newShortcutCAS.activated.connect(self.showHistory) self.connect(self, SIGNAL('userListActivated(int, const QString)'), self.completion_list_selected) def showHistory(self): self.showUserList(1, QStringList(self.history)) def autoComplete(self): self.autoCompleteFromAll() def clearConsole(self): """Clear the contents of the console.""" self.setText('') self.insertInitText() self.displayPrompt(False) self.setFocus() def commandConsole(self, command): if not self.is_cursor_on_last_line(): self.move_cursor_to_end() line, pos = self.getCurLine() selCmd = self.text(line).length() self.setSelection(line, 4, line, selCmd) self.removeSelectedText() if command == "iface": """Import QgisInterface class""" self.append('from qgis.utils import iface') self.move_cursor_to_end() elif command == "sextante": """Import Sextante class""" self.append('from sextante.core.Sextante import Sextante') self.move_cursor_to_end() elif command == "cLayer": """Retrive current Layer from map camvas""" self.append('cLayer = iface.mapCanvas().currentLayer()') self.move_cursor_to_end() self.setFocus() def setLexers(self, lexer): if lexer: font = QFont() font.setFamily('Mono') ## Courier New font.setFixedPitch(True) ## check platform for font size if sys.platform.startswith('darwin'): font.setPointSize(13) else: font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) self.lexer = QsciLexerPython() self.lexer.setDefaultFont(font) self.lexer.setColor(Qt.red, 1) self.lexer.setColor(Qt.darkGreen, 5) self.lexer.setColor(Qt.darkBlue, 15) self.lexer.setFont(font, 1) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.api = QsciAPIs(self.lexer) self.api.loadPrepared( QString(os.path.dirname(__file__) + "/api/pyqgis_master.pap")) self.setLexer(self.lexer) ## TODO: show completion list for file and directory def completion_list_selected(self, id, txt): if id == 1: txt = unicode(txt) # get current cursor position line, pos = self.getCurLine() selCmd = self.text(line).length() # select typed text self.setSelection(line, 4, line, selCmd) self.removeSelectedText() self.insert(txt) def insertInitText(self): #self.setLexers(False) txtInit = QCoreApplication.translate( "PythonConsole", "## To access Quantum GIS environment from this console\n" "## use qgis.utils.iface object (instance of QgisInterface class). Read help for more info.\n\n" ) initText = self.setText(txtInit) def getCurrentPos(self): """ Get the position (as an int) of the cursor. getCursorPosition() returns a (linenr, index) tuple. """ return self.SendScintilla(self.SCI_GETCURRENTPOS) def getText(self): """ Get the text as a unicode string. """ value = self.getBytes().decode('utf-8') # print (value) printing can give an error because the console font # may not have all unicode characters return value def getBytes(self): """ Get the text as bytes (utf-8 encoded). This is how the data is stored internally. """ len = self.SendScintilla(self.SCI_GETLENGTH) + 1 bb = QByteArray(len, '0') N = self.SendScintilla(self.SCI_GETTEXT, len, bb) return bytes(bb)[:-1] def getTextLength(self): return self.SendScintilla(QsciScintilla.SCI_GETLENGTH) def getLine(self, linenr): """ Get the bytes on the given line number. """ len = self.SendScintilla(QsciScintilla.SCI_LINELENGTH) + 1 bb = QByteArray(len, '0') N = self.SendScintilla(QsciScintilla.SCI_GETLINE, len, bb) return bytes(bb)[:-1] def getCurLine(self): """ Get the current line (as a string) and the position of the cursor in it. """ linenr, index = self.getCursorPosition() #line = self.getLine(linenr) #.decode('utf-8') return linenr, index def get_end_pos(self): """Return (line, index) position of the last character""" line = self.lines() - 1 return (line, self.text(line).length()) def is_cursor_at_end(self): """Return True if cursor is at the end of text""" cline, cindex = self.getCursorPosition() return (cline, cindex) == self.get_end_pos() def move_cursor_to_end(self): """Move cursor to end of text""" line, index = self.get_end_pos() self.setCursorPosition(line, index) self.ensureCursorVisible() def on_new_line(self): """On new input line""" self.move_cursor_to_end() self.current_prompt_pos = self.getCursorPosition() self.new_input_line = False def is_cursor_on_last_line(self): """Return True if cursor is on the last line""" cline, _ = self.getCursorPosition() return cline == self.lines() - 1 def new_prompt(self, prompt): """ Print a new prompt and save its (line, index) position """ self.write(prompt, prompt=True) # now we update our cursor giving end of prompt self.current_prompt_pos = self.getCursorPosition() self.ensureCursorVisible() def check_selection(self): """ Check if selected text is r/w, otherwise remove read-only parts of selection """ #if self.current_prompt_pos is None: #self.move_cursor_to_end() #return line_from, index_from, line_to, index_to = self.getSelection() pline, pindex = self.getCursorPosition() if line_from < pline or \ (line_from == pline and index_from < pindex): self.setSelection(pline, pindex, line_to, index_to) def displayPrompt(self, more=False): self.append("... ") if more else self.append(">>> ") self.move_cursor_to_end() def updateHistory(self, command): if isinstance(command, QStringList): for line in command: self.history.append(line) elif not command == "": if len(self.history) <= 0 or \ not command == self.history[-1]: self.history.append(command) self.historyIndex = len(self.history) def writeHistoryFile(self): wH = open(_historyFile, 'w') for s in self.history: wH.write(s + '\n') wH.close() def readHistoryFile(self): fileExist = QFile.exists(_historyFile) if fileExist: rH = open(_historyFile, 'r') for line in rH: if line != "\n": l = line.rstrip('\n') self.updateHistory(l) else: return def clearHistoryFile(self): cH = open(_historyFile, 'w') cH.close() def showPrevious(self): if self.historyIndex < len( self.history) and not self.history.isEmpty(): line, pos = self.getCurLine() selCmd = self.text(line).length() self.setSelection(line, 4, line, selCmd) self.removeSelectedText() self.historyIndex += 1 if self.historyIndex == len(self.history): self.insert("") pass else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def showNext(self): if self.historyIndex > 0 and not self.history.isEmpty(): line, pos = self.getCurLine() selCmd = self.text(line).length() self.setSelection(line, 4, line, selCmd) self.removeSelectedText() self.historyIndex -= 1 if self.historyIndex == len(self.history): self.insert("") else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def keyPressEvent(self, e): linenr, index = self.getCurLine() if not self.is_cursor_on_last_line() or index < 4: if e.modifiers() & Qt.ControlModifier or e.modifiers( ) & Qt.MetaModifier: if e.key() == Qt.Key_C or e.key() == Qt.Key_A: QsciScintilla.keyPressEvent(self, e) else: # all other keystrokes get sent to the input line self.move_cursor_to_end() #pass else: if (e.key() == Qt.Key_Return or e.key() == Qt.Key_Enter) and not self.isListActive(): self.entered() elif e.modifiers() & Qt.ControlModifier: if e.key() == Qt.Key_V: self.paste() elif e.key() == Qt.Key_C: self.copy() elif e.key() == Qt.Key_X: self.cut() elif e.key() == Qt.Key_Left: e.accept() if e.modifiers() & Qt.ShiftModifier: if index > 4: if e.modifiers() & Qt.ControlModifier: self.SendScintilla( QsciScintilla.SCI_WORDLEFTEXTEND) else: self.SendScintilla( QsciScintilla.SCI_CHARLEFTEXTEND) else: if index > 4: if e.modifiers() & Qt.ControlModifier: self.SendScintilla(QsciScintilla.SCI_WORDLEFT) else: self.SendScintilla(QsciScintilla.SCI_CHARLEFT) elif e.key() == Qt.Key_Right: e.accept() if e.modifiers() & Qt.ShiftModifier: if index >= 4: if e.modifiers() & Qt.ControlModifier: self.SendScintilla( QsciScintilla.SCI_WORDRIGHTEXTEND) else: self.SendScintilla( QsciScintilla.SCI_CHARRIGHTEXTEND) else: if index >= 4: if e.modifiers() & Qt.ControlModifier: self.SendScintilla(QsciScintilla.SCI_WORDRIGHT) else: self.SendScintilla(QsciScintilla.SCI_CHARRIGHT) elif e.key() == Qt.Key_Backspace: curPos, pos = self.getCursorPosition() line = self.lines() - 1 if curPos < line - 1 or pos < 5: return #else: #self.move_cursor_to_end() QsciScintilla.keyPressEvent(self, e) elif e.key() == Qt.Key_Delete: if self.hasSelectedText(): self.removeSelectedText() elif self.is_cursor_on_last_line(): self.SendScintilla(QsciScintilla.SCI_CLEAR) e.accept() elif e.key() == Qt.Key_Home: self.setCursorPosition(linenr, 4) self.ensureCursorVisible() elif e.key() == Qt.Key_Down and not self.isListActive(): self.showPrevious() elif e.key() == Qt.Key_Up and not self.isListActive(): self.showNext() ## TODO: press event for auto-completion file directory else: QsciScintilla.keyPressEvent(self, e) def paste(self): """Reimplement QScintilla method""" stringPaste = unicode(QApplication.clipboard().text()) if self.hasSelectedText(): self.removeSelectedText() self.insertFromDropPaste(stringPaste) ## Drag and drop def dropEvent(self, e): if e.mimeData().hasText(): stringDrag = e.mimeData().text() self.insertFromDropPaste(stringDrag) e.setDropAction(Qt.MoveAction) e.accept() else: QsciScintillaCompat.dropEvent(self, e) def insertFromDropPaste(self, textDP): pasteList = QStringList() pasteList = textDP.split("\n") for line in pasteList[:-1]: self.insert(line) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) self.runCommand(unicode(self.currentCommand())) self.insert(unicode(pasteList[-1])) self.move_cursor_to_end() def getTextFromEditor(self): text = self.text() textList = QStringList() textList = text.split("\n") return textList def insertTextFromFile(self, listOpenFile): for line in listOpenFile[:-1]: self.append(line) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) self.runCommand(unicode(self.currentCommand())) self.append(unicode(listOpenFile[-1])) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def entered(self): self.move_cursor_to_end() self.runCommand(unicode(self.currentCommand())) self.setFocus() #self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER) def currentCommand(self): linenr, index = self.getCurLine() #for i in range(0, linenr): txtLength = self.text(linenr).length() string = self.text() cmdLine = string.right(txtLength - 4) cmd = str(cmdLine) return cmd def runCommand(self, cmd): self.updateHistory(cmd) self.SendScintilla(QsciScintilla.SCI_NEWLINE) if cmd in ('_save', '_clear', '_clearAll'): if cmd == '_save': self.writeHistoryFile() print QCoreApplication.translate( "PythonConsole", "## History saved successfully ##") elif cmd == '_clear': self.clearHistoryFile() print QCoreApplication.translate( "PythonConsole", "## History cleared successfully ##") elif cmd == '_clearAll': res = QMessageBox.question( self, "Python Console", QCoreApplication.translate( "PythonConsole", "Are you sure you want to completely\n" "delete the command history ?"), QMessageBox.Yes | QMessageBox.No) if res == QMessageBox.No: self.SendScintilla(QsciScintilla.SCI_DELETEBACK) return self.history = QStringList() self.clearHistoryFile() print QCoreApplication.translate( "PythonConsole", "## History cleared successfully ##") output = sys.stdout.get_and_clean_data() if output: self.append(output) self.displayPrompt(False) else: self.buffer.append(cmd) src = "\n".join(self.buffer) more = self.runsource(src, "<input>") if not more: self.buffer = [] output = sys.stdout.get_and_clean_data() if output: self.append(output) self.move_cursor_to_end() self.displayPrompt(more) def write(self, txt): self.SendScintilla(QsciScintilla.SCI_SETSTYLING, len(txt), 1) self.append(txt) self.SendScintilla(QsciScintilla.SCI_SETSTYLING, len(txt), 1)
class EditorOutput(QsciScintilla): def __init__(self, parent=None): #QsciScintilla.__init__(self, parent) super(EditorOutput, self).__init__(parent) self.parent = parent self.edit = self.parent.edit # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "traceback") self.insertInitText() self.setLexers() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor("#fcf3ed")) self.setMinimumHeight(120) # Folding #self.setFolding(QsciScintilla.BoxedTreeFoldStyle) #self.setFoldMarginColors(QColor("#99CC66"),QColor("#333300")) #self.setWrapMode(QsciScintilla.WrapCharacter) ## Edge Mode #self.setEdgeMode(QsciScintilla.EdgeLine) #self.setEdgeColumn(80) #self.setEdgeColor(QColor("#FF0000")) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runShortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runShortcut.activated.connect(self.enteredSelected) # Reimplemeted copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate( "PythonConsole", "## To access Quantum GIS environment from this console\n" "## use qgis.utils.iface object (instance of QgisInterface class). Read help for more info.\n\n" ) initText = self.setText(txtInit) def refreshLexerProperties(self): self.setLexers() def setLexers(self): self.lexer = QsciLexerPython() settings = QSettings() loadFont = settings.value("pythonConsole/fontfamilytext", "Monospace").toString() fontSize = settings.value("pythonConsole/fontsize", 10).toInt()[0] font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) self.lexer.setDefaultFont(font) self.lexer.setColor(Qt.red, 1) self.lexer.setColor(Qt.darkGreen, 5) self.lexer.setColor(Qt.darkBlue, 15) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.setLexer(self.lexer) def getTextFromEditor(self): text = self.text() textList = text.split("\n") return textList def clearConsole(self): #self.SendScintilla(QsciScintilla.SCI_CLEARALL) self.setText('') self.insertInitText() self.edit.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QIcon(":/images/console/iconRunConsole.png") iconPastebin = QIcon(":/images/console/iconCodepadConsole.png") iconClear = QIcon(":/images/console/iconClearConsole.png") iconHideTool = QIcon(":/images/console/iconHideToolConsole.png") hideToolBar = menu.addAction(iconHideTool, "Hide/Show Toolbar", self.hideToolBar) menu.addSeparator() runAction = menu.addAction(iconRun, "Enter Selected", self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(iconClear, "Clear console", self.clearConsole) menu.addSeparator() copyAction = menu.addAction("Copy", self.copy, QKeySequence.Copy) pastebinAction = menu.addAction(iconPastebin, "Share on codepad", self.pastebin) menu.addSeparator() selectAllAction = menu.addAction("Select All", self.selectAll, QKeySequence.SelectAll) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) pastebinAction.setEnabled(False) selectAllAction.setEnabled(False) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) pastebinAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) action = menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.edit.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = unicode(self.selectedText()) text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: self.emit(SIGNAL("keyboard_interrupt()")) def enteredSelected(self): cmd = self.selectedText() self.edit.insertFromDropPaste(cmd) self.edit.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if txt.length() and txt >= " ": self.edit.append(txt) self.edit.move_cursor_to_end() self.edit.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def pastebin(self): import urllib2, urllib listText = self.selectedText().split('\n') getCmd = [] for strLine in listText: if strLine != "": #if s[0:3] in (">>>", "..."): # filter for special command (_save,_clear) and comment if strLine[4] != "_" and strLine[:2] != "##": strLine.replace(">>> ", "").replace("... ", "") getCmd.append(unicode(strLine)) pasteText = u"\n".join(getCmd) url = 'http://codepad.org' values = {'lang': 'Python', 'code': pasteText, 'submit': 'Submit'} try: response = urllib2.urlopen(url, urllib.urlencode(values)) url = response.read() for href in url.split("</a>"): if "Link:" in href: ind = href.index('Link:') found = href[ind + 5:] for i in found.split('">'): if '<a href=' in i: link = i.replace('<a href="', "").strip() if link: QApplication.clipboard().setText(link) print "## URL copied to clipboard ##" except urllib2.URLError, e: print "## Connection error ##" print "## " + str(e.args) + " ##"
class EditorOutput(QsciScintilla): def __init__(self, parent=None): #QsciScintilla.__init__(self, parent) super(EditorOutput,self).__init__(parent) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "traceback") self.edit = PythonEdit() self.setLexers() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor("#fcf3ed")) # Folding #self.setFolding(QsciScintilla.BoxedTreeFoldStyle) #self.setFoldMarginColors(QColor("#99CC66"),QColor("#333300")) #self.setWrapMode(QsciScintilla.WrapCharacter) ## Edge Mode : does not seems to work #self.setEdgeMode(QsciScintilla.EdgeLine) #self.setEdgeColumn(80) #self.setEdgeColor(QColor("#FF0000")) self.SendScintilla(QsciScintilla.SCI_SETWRAPMODE, 2) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) def refreshLexerProperties(self): self.setLexers() def setLexers(self): self.lexer = QsciLexerPython() settings = QSettings() loadFont = settings.value("pythonConsole/fontfamilytext").toString() fontSize = settings.value("pythonConsole/fontsize").toInt()[0] font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) self.lexer.setDefaultFont(font) self.lexer.setColor(Qt.red, 1) self.lexer.setColor(Qt.darkGreen, 5) self.lexer.setColor(Qt.darkBlue, 15) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.setLexer(self.lexer) def getTextFromEditor(self): text = self.text() textList = text.split("\n") return textList def clearConsole(self): #self.SendScintilla(QsciScintilla.SCI_CLEARALL) self.setText('') def contextMenuEvent(self, e): menu = QMenu(self) runAction = menu.addAction("Enter Selected") copyAction = menu.addAction("Copy CTRL+C") runAction.setEnabled(False) if self.hasSelectedText(): runAction.setEnabled(True) action = menu.exec_(self.mapToGlobal(e.pos())) if action == runAction: cmd = self.selectedText() self.edit.insertFromDropPaste(cmd) self.edit.entered() if action == copyAction: self.copy() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = unicode(self.selectedText()) text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: self.emit(SIGNAL("keyboard_interrupt()"))
class ScintillaPythonEditBox(QsciScintilla, BasePythonEditBox): def __init__(self, parent=None): QsciScintilla.__init__(self, parent) self.lexer = None self.api = None self.lexerType = -1 self.setCommonOptions() self.initShortcuts() def setCommonOptions(self): # Enable non-ASCII characters self.setUtf8(True) # Default font # Load font from Python console settings settings = QSettings() fontName = settings.value('pythonConsole/fontfamilytext', 'Monospace') fontSize = int(settings.value('pythonConsole/fontsize', 10)) self.defaultFont = QFont(fontName) self.defaultFont.setFixedPitch(True) self.defaultFont.setPointSize(fontSize) self.defaultFont.setStyleHint(QFont.TypeWriter) self.defaultFont.setStretch(QFont.SemiCondensed) self.defaultFont.setLetterSpacing(QFont.PercentageSpacing, 87.0) self.defaultFont.setBold(False) self.boldFont = QFont(self.defaultFont) self.boldFont.setBold(True) self.italicFont = QFont(self.defaultFont) self.italicFont.setItalic(True) self.setFont(self.defaultFont) self.setMarginsFont(self.defaultFont) self.setFont(self.defaultFont) self.setMarginsFont(self.defaultFont) self.initLexer() self.setBraceMatching(QsciScintilla.SloppyBraceMatch) self.setWrapMode(QsciScintilla.WrapWord) self.setWrapVisualFlags(QsciScintilla.WrapFlagByText, QsciScintilla.WrapFlagNone, 4) self.setSelectionForegroundColor(QColor('#2e3436')) self.setSelectionBackgroundColor(QColor('#babdb6')) # Show line numbers self.setMarginWidth(1, '000') self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor('#2e3436')) self.setMarginsBackgroundColor(QColor('#babdb6')) # Highlight current line self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor('#d3d7cf')) # Mark column 80 with vertical line self.setEdgeMode(QsciScintilla.EdgeLine) self.setEdgeColumn(80) self.setEdgeColor(QColor('#eeeeec')) # Indentation self.setAutoIndent(True) self.setIndentationsUseTabs(False) self.setIndentationWidth(4) self.setTabIndents(True) self.setBackspaceUnindents(True) self.setTabWidth(4) # Autocomletion self.setAutoCompletionThreshold(2) self.setAutoCompletionSource(QsciScintilla.AcsAll) def initShortcuts(self): (ctrl, shift) = (self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16) # Disable some shortcuts self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl + shift) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T') + ctrl) #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Z") + ctrl) #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Y") + ctrl) # Use Ctrl+Space for autocompletion self.shortcutAutocomplete = QShortcut( QKeySequence(Qt.CTRL + Qt.Key_Space), self) self.shortcutAutocomplete.setContext(Qt.WidgetShortcut) self.shortcutAutocomplete.activated.connect(self.autoComplete) def autoComplete(self): self.autoCompleteFromAll() def setLexerType(self, lexerType): self.lexerType = lexerType self.initLexer() def initLexer(self): self.lexer = QsciLexerPython() colorDefault = QColor('#2e3436') colorComment = QColor('#c00') colorCommentBlock = QColor('#3465a4') colorNumber = QColor('#4e9a06') colorType = QColor('#4e9a06') colorKeyword = QColor('#204a87') colorString = QColor('#ce5c00') self.lexer.setDefaultFont(self.defaultFont) self.lexer.setDefaultColor(colorDefault) self.lexer.setColor(colorComment, 1) self.lexer.setColor(colorNumber, 2) self.lexer.setColor(colorString, 3) self.lexer.setColor(colorString, 4) self.lexer.setColor(colorKeyword, 5) self.lexer.setColor(colorString, 6) self.lexer.setColor(colorString, 7) self.lexer.setColor(colorType, 8) self.lexer.setColor(colorCommentBlock, 12) self.lexer.setColor(colorString, 15) self.lexer.setFont(self.italicFont, 1) self.lexer.setFont(self.boldFont, 5) self.lexer.setFont(self.boldFont, 8) self.lexer.setFont(self.italicFont, 12) self.api = QsciAPIs(self.lexer) settings = QSettings() useDefaultAPI = bool(settings.value('pythonConsole/preloadAPI', True)) if useDefaultAPI: # Load QGIS API shipped with Python console self.api.loadPrepared( os.path.join(QgsApplication.pkgDataPath(), 'python', 'qsci_apis', 'pyqgis.pap')) else: # Load user-defined API files apiPaths = settings.value('pythonConsole/userAPI', []) for path in apiPaths: self.api.load(path) self.api.prepare() self.lexer.setAPIs(self.api) self.setLexer(self.lexer) #impliment base editor def insertPlainText(self, text): self.insert(text) pos = self.getCursorPosition() self.setCursorPosition(pos[0], pos[1] + len(text)) def toPlainText(self): return self.text() def get_font_size(self): return self.font().pointSize() def set_font_size(self, new_point_size): font = self.font() self.setFont(QFont(font.family(), new_point_size)) def wheelEvent(self, event): if event.modifiers() == Qt.ControlModifier: self.emit(SIGNAL("wheelEvent(QWheelEvent)"), event) else: super(ScintillaPythonEditBox, self).wheelEvent(event)
class ScintillaPythonEditBox(QsciScintilla, BasePythonEditBox): def __init__(self, parent=None): QsciScintilla.__init__(self, parent) self.lexer = None self.api = None self.lexerType = -1 self.setCommonOptions() self.initShortcuts() def setCommonOptions(self): # Enable non-ASCII characters self.setUtf8(True) # Default font # Load font from Python console settings settings = QSettings() fontName = settings.value('pythonConsole/fontfamilytext', 'Monospace') fontSize = int(settings.value('pythonConsole/fontsize', 10)) self.defaultFont = QFont(fontName) self.defaultFont.setFixedPitch(True) self.defaultFont.setPointSize(fontSize) self.defaultFont.setStyleHint(QFont.TypeWriter) self.defaultFont.setStretch(QFont.SemiCondensed) self.defaultFont.setLetterSpacing(QFont.PercentageSpacing, 87.0) self.defaultFont.setBold(False) self.boldFont = QFont(self.defaultFont) self.boldFont.setBold(True) self.italicFont = QFont(self.defaultFont) self.italicFont.setItalic(True) self.setFont(self.defaultFont) self.setMarginsFont(self.defaultFont) self.setFont(self.defaultFont) self.setMarginsFont(self.defaultFont) self.initLexer() self.setBraceMatching(QsciScintilla.SloppyBraceMatch) self.setWrapMode(QsciScintilla.WrapWord) self.setWrapVisualFlags(QsciScintilla.WrapFlagByText, QsciScintilla.WrapFlagNone, 4) self.setSelectionForegroundColor(QColor('#2e3436')) self.setSelectionBackgroundColor(QColor('#babdb6')) # Show line numbers self.setMarginWidth(1, '000') self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor('#2e3436')) self.setMarginsBackgroundColor(QColor('#babdb6')) # Highlight current line self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor('#d3d7cf')) # Mark column 80 with vertical line self.setEdgeMode(QsciScintilla.EdgeLine) self.setEdgeColumn(80) self.setEdgeColor(QColor('#eeeeec')) # Indentation self.setAutoIndent(True) self.setIndentationsUseTabs(False) self.setIndentationWidth(4) self.setTabIndents(True) self.setBackspaceUnindents(True) self.setTabWidth(4) # Autocomletion self.setAutoCompletionThreshold(2) self.setAutoCompletionSource(QsciScintilla.AcsAll) def initShortcuts(self): (ctrl, shift) = (self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16) # Disable some shortcuts self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl + shift) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T') + ctrl) #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Z") + ctrl) #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Y") + ctrl) # Use Ctrl+Space for autocompletion self.shortcutAutocomplete = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Space), self) self.shortcutAutocomplete.setContext(Qt.WidgetShortcut) self.shortcutAutocomplete.activated.connect(self.autoComplete) def autoComplete(self): self.autoCompleteFromAll() def setLexerType(self, lexerType): self.lexerType = lexerType self.initLexer() def initLexer(self): self.lexer = QsciLexerPython() colorDefault = QColor('#2e3436') colorComment = QColor('#c00') colorCommentBlock = QColor('#3465a4') colorNumber = QColor('#4e9a06') colorType = QColor('#4e9a06') colorKeyword = QColor('#204a87') colorString = QColor('#ce5c00') self.lexer.setDefaultFont(self.defaultFont) self.lexer.setDefaultColor(colorDefault) self.lexer.setColor(colorComment, 1) self.lexer.setColor(colorNumber, 2) self.lexer.setColor(colorString, 3) self.lexer.setColor(colorString, 4) self.lexer.setColor(colorKeyword, 5) self.lexer.setColor(colorString, 6) self.lexer.setColor(colorString, 7) self.lexer.setColor(colorType, 8) self.lexer.setColor(colorCommentBlock, 12) self.lexer.setColor(colorString, 15) self.lexer.setFont(self.italicFont, 1) self.lexer.setFont(self.boldFont, 5) self.lexer.setFont(self.boldFont, 8) self.lexer.setFont(self.italicFont, 12) self.api = QsciAPIs(self.lexer) settings = QSettings() useDefaultAPI = bool(settings.value('pythonConsole/preloadAPI', True)) if useDefaultAPI: # Load QGIS API shipped with Python console self.api.loadPrepared( os.path.join(QgsApplication.pkgDataPath(), 'python', 'qsci_apis', 'pyqgis.pap')) else: # Load user-defined API files apiPaths = settings.value('pythonConsole/userAPI', []) for path in apiPaths: self.api.load(path) self.api.prepare() self.lexer.setAPIs(self.api) self.setLexer(self.lexer) #impliment base editor def insertPlainText(self, text): self.insert(text) pos = self.getCursorPosition() self.setCursorPosition(pos[0], pos[1]+len(text)) def toPlainText(self): return self.text() def get_font_size(self): return self.font().pointSize() def set_font_size(self, new_point_size): font = self.font() self.setFont(QFont(font.family(), new_point_size)) def wheelEvent(self, event): if event.modifiers() == Qt.ControlModifier: self.emit(SIGNAL("wheelEvent(QWheelEvent)"), event) else: super(ScintillaPythonEditBox, self).wheelEvent(event)
class ShellOutputScintilla(QsciScintilla): def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "traceback") self.insertInitText() self.setLexers() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor("#fcf3ed")) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) # Reimplemeted copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate( "PythonConsole", "Python %1 on %2\n" "## Type help(iface) for more info and list of methods.\n").arg( sys.version, socket.gethostname()) initText = self.setText(txtInit) def refreshLexerProperties(self): self.setLexers() def setLexers(self): self.lexer = QsciLexerPython() settings = QSettings() loadFont = settings.value("pythonConsole/fontfamilytext", "Monospace").toString() fontSize = settings.value("pythonConsole/fontsize", 10).toInt()[0] font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) self.lexer.setDefaultFont(font) self.lexer.setColor(Qt.red, 1) self.lexer.setColor(Qt.darkGreen, 5) self.lexer.setColor(Qt.darkBlue, 15) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/iconRunConsole.png") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.png") iconHideTool = QgsApplication.getThemeIcon( "console/iconHideToolConsole.png") hideToolBar = menu.addAction(iconHideTool, "Hide/Show Toolbar", self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction("Show Editor", self.showEditor) menu.addSeparator() runAction = menu.addAction(iconRun, "Enter Selected", self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(iconClear, "Clear console", self.clearConsole) menu.addSeparator() copyAction = menu.addAction("Copy", self.copy, QKeySequence.Copy) menu.addSeparator() selectAllAction = menu.addAction("Select All", self.selectAll, QKeySequence.SelectAll) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) action = menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.widgetEditor if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = unicode(self.selectedText()) text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: self.emit(SIGNAL("keyboard_interrupt()")) def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if txt.length() and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage('Console', text, QgsMessageBar.INFO, timeout)