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.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, type=bool) if chekBoxAPI: self.api.loadPrepared( QgsApplication.pkgDataPath() + "/python/qsci_apis/pyqgis_master.pap" ) 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)
def active_code_completion(self, enabled=True): if self.api is not None and enabled: return if enabled: self.api = QsciAPIs(self._lexer) if settings.get_setting('editor/completion-keywords'): for keyword in keywords.keywords: self.api.add(keyword) self.api.prepare() source = QsciScintilla.AcsAPIs if settings.get_setting('editor/completion-document'): source = QsciScintilla.AcsAll elif settings.get_setting('editor/completion-document'): source = QsciScintilla.AcsDocument else: source = QsciScintilla.AcsNone threshold = settings.get_setting('editor/completion-threshold') self.setAutoCompletionThreshold(threshold) self.setAutoCompletionSource(source) cs = settings.get_setting('editor/completion-cs') self.setAutoCompletionCaseSensitivity(cs) repl_word = settings.get_setting('editor/completion-replace-word') self.setAutoCompletionReplaceWord(repl_word) show_single = settings.get_setting('editor/completion-single') use_single = 2 if show_single else 0 self.setAutoCompletionUseSingle(use_single) else: self.api = None self.setAutoCompletionSource(0)
def __init__(self, parent=None): super(RevsetEntry, self).__init__(parent) self.setMarginWidth(1, 0) self.setReadOnly(False) self.setUtf8(True) self.setCaretWidth(10) self.setCaretLineBackgroundColor(QColor("#e6fff0")) self.setCaretLineVisible(True) self.setAutoIndent(True) self.setMatchedBraceBackgroundColor(Qt.yellow) self.setIndentationsUseTabs(False) self.setBraceMatching(QsciScintilla.SloppyBraceMatch) self.setWrapMode(QsciScintilla.WrapWord) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) sp = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) sp.setHorizontalStretch(1) sp.setVerticalStretch(0) self.setSizePolicy(sp) self.setAutoCompletionThreshold(2) self.setAutoCompletionSource(QsciScintilla.AcsAPIs) self.setAutoCompletionFillupsEnabled(True) self.setLexer(QsciLexerPython(self)) self.lexer().setFont(qtlib.getfont('fontcomment').font()) self.apis = QsciAPIs(self.lexer())
class PrepareAPIDialog(QDialog): def __init__(self, api_lexer, api_files, pap_file, parent=None): QDialog.__init__(self, parent) self.ui = Ui_APIsDialogPythonConsole() self.ui.setupUi(self) self.setWindowTitle(QCoreApplication.translate("PythonConsole", "Compile APIs")) self.ui.plainTextEdit.setVisible(False) self.ui.textEdit_Qsci.setVisible(False) self.adjustSize() self._api = None self.ui.buttonBox.rejected.connect(self._stopPreparation) self._api_files = api_files self._api_lexer = api_lexer self._pap_file = pap_file def _clearLexer(self): # self.ui.textEdit_Qsci.setLexer(0) self.qlexer = None def _stopPreparation(self): if self._api is not None: self._api.cancelPreparation() self._api = None self._clearLexer() self.close() def _preparationFinished(self): self._clearLexer() if os.path.exists(self._pap_file): os.remove(self._pap_file) self.ui.label.setText(QCoreApplication.translate("PythonConsole", "Saving prepared file...")) prepd = self._api.savePrepared(unicode(self._pap_file)) rslt = self.trUtf8("Error") if prepd: rslt = QCoreApplication.translate("PythonConsole", "Saved") self.ui.label.setText(u"{0} {1}".format(self.ui.label.text(), rslt)) self._api = None self.ui.progressBar.setVisible(False) self.ui.buttonBox.button(QDialogButtonBox.Cancel).setText(QCoreApplication.translate("PythonConsole", "Done")) self.adjustSize() def prepareAPI(self): # self.ui.textEdit_Qsci.setLexer(0) exec(u"self.qlexer = {0}(self.ui.textEdit_Qsci)".format(self._api_lexer)) # self.ui.textEdit_Qsci.setLexer(self.qlexer) self._api = QsciAPIs(self.qlexer) self._api.apiPreparationFinished.connect(self._preparationFinished) for api_file in self._api_files: self._api.load(unicode(api_file)) try: self._api.prepare() except Exception as err: self._api = None self._clearLexer() self.ui.label.setText(QCoreApplication.translate("PythonConsole", "Error preparing file...")) self.ui.progressBar.setVisible(False) self.ui.plainTextEdit.setVisible(True) self.ui.plainTextEdit.insertPlainText(err) self.ui.buttonBox.button(QDialogButtonBox.Cancel).setText(self.trUtf8("Done")) self.adjustSize()
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.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, type=bool) chekBoxPreparedAPI = self.settings.value( "pythonConsole/usePreparedAPIFile", False, type=bool) if chekBoxAPI: apisdir = os.path.join(QgsApplication.pkgDataPath(), "python", "qsci_apis") pap = os.path.join(apisdir, "pyqgis.pap") mpap = os.path.join(apisdir, "pyqgis-master.pap") if os.path.exists( mpap): # override installed with master .pap build pap = mpap if QgsApplication.isRunningFromBuildDir(): bdir = os.path.dirname(QgsApplication.buildOutputPath()) bpap = os.path.join(bdir, "python", "qsci_apis", "pyqgis-master.pap") if os.path.exists(bpap): # if not generated .pap exists, else fall back to preprepared one pap = bpap 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)
def __init__(self, nombre_archivo, ext=''): super(Editor, self).__init__() self.__nombre = "" self.texto_modificado = False self.es_nuevo = True self.guardado_actualmente = False # Actualiza flags (espacios en blanco, cursor, sidebar, etc) self.actualizar() # Lexer self._lexer = None self.cargar_lexer(ext) # Autocompletado #FIXME: api = QsciAPIs(self._lexer) for palabra in keywords.keywords: api.add(palabra) api.prepare() self.setAutoCompletionThreshold(1) self.setAutoCompletionSource(QsciScintilla.AcsAPIs) # Indentación self._indentacion = ESettings.get('editor/indentacionAncho') self.send("sci_settabwidth", self._indentacion) # Minimapa self.minimapa = MiniMapa(self) self.connect(self, SIGNAL("selectionChanged()"), self.minimapa.area) self.connect(self, SIGNAL("textChanged()"), self.minimapa.actualizar_codigo) # Thread ocurrencias self.hilo_ocurrencias = ThreadBusqueda() self.connect(self.hilo_ocurrencias, SIGNAL("ocurrenciasThread(PyQt_PyObject)"), self.marcar_palabras) # Analizador de estilo de código self.checker = checker.Checker(self) self.connect(self.checker, SIGNAL("finished()"), self._show_violations) #self.checker.errores.connect(self._marcar_errores) # Fuente fuente = ESettings.get('editor/fuente') tam_fuente = ESettings.get('editor/fuenteTam') self.cargar_fuente(fuente, tam_fuente) self.setMarginsBackgroundColor(QColor(self._tema['sidebar-fondo'])) self.setMarginsForegroundColor(QColor(self._tema['sidebar-fore'])) # Línea actual, cursor self.caret_line(self._tema['caret-background'], self._tema['caret-line'], self._tema['caret-opacidad']) # Márgen if ESettings.get('editor/margen'): self.actualizar_margen() # Brace matching self.match_braces(Base.SloppyBraceMatch) self.match_braces_color(self._tema['brace-background'], self._tema['brace-foreground']) self.unmatch_braces_color(self._tema['brace-unbackground'], self._tema['brace-unforeground'])
def prepareAPI(self): try: self._api = QsciAPIs(self._api_lexer) self._api.apiPreparationFinished.connect(self._preparationFinished) for api_file in self._api_files: self._api.load(unicode(api_file)) self._api.prepare() except Exception as err: self._api = None sys.exit(1)
def initLexer(self): if self.lexerType == self.LEXER_PYTHON: 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) elif self.lexerType == self.LEXER_R: # R lexer self.lexer = LexerR() self.setLexer(self.lexer)
class RevsetEntry(QsciScintilla): returnPressed = pyqtSignal() def __init__(self, parent=None): super(RevsetEntry, self).__init__(parent) self.setMarginWidth(1, 0) self.setReadOnly(False) self.setUtf8(True) self.setCaretWidth(10) self.setCaretLineBackgroundColor(QColor("#e6fff0")) self.setCaretLineVisible(True) self.setAutoIndent(True) self.setMatchedBraceBackgroundColor(Qt.yellow) self.setIndentationsUseTabs(False) self.setBraceMatching(QsciScintilla.SloppyBraceMatch) self.setWrapMode(QsciScintilla.WrapWord) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) sp = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) sp.setHorizontalStretch(1) sp.setVerticalStretch(0) self.setSizePolicy(sp) self.setAutoCompletionThreshold(2) self.setAutoCompletionSource(QsciScintilla.AcsAPIs) self.setAutoCompletionFillupsEnabled(True) self.setLexer(QsciLexerPython(self)) self.lexer().setFont(qtlib.getfont('fontcomment').font()) self.apis = QsciAPIs(self.lexer()) def addCompletions(self, *lists): for list in lists: for x, y in list: self.apis.add(x) self.apis.prepare() def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: event.ignore() return if event.key() in (Qt.Key_Enter, Qt.Key_Return): if not self.isListActive(): event.ignore() self.returnPressed.emit() return super(RevsetEntry, self).keyPressEvent(event) def sizeHint(self): return QSize(10, self.fontMetrics().height())
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)
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)
def __init__(self, parent=None, line_num_margin=3, autocomplete_list=None): super(PythonEditor, self).__init__(parent, line_num_margin, autocomplete_list) # Set Python lexer self.lexer = QsciLexerPython(self) self.lexer.setDefaultFont(self.editor_font) self.lexer.setFont(self.editor_font, QsciLexerPython.Comment) # Indentation warning ("The indentation is inconsistent when compared to the previous line") self.lexer.setIndentationWarning(QsciLexerPython.Inconsistent) # Set auto-completion self.api = QsciAPIs(self.lexer) if autocomplete_list is not None: # Add additional completion strings for i in autocomplete_list: self.api.add(i) self.api.prepare() self.setAutoCompletionThreshold(3) self.setAutoCompletionSource(QsciScintilla.AcsAll) self.setAutoCompletionUseSingle(QsciScintilla.AcusExplicit) self.setLexer(self.lexer) # PEP8 tabs self.setIndentationsUseTabs(False) self.setAutoIndent(True) self.setIndentationGuides(True) # PEP8 edge column line self.edgecol = 80 # Linters self.linter = 'internal'
def __init__(self, language, forPreparation = False, parent = None): """ Constructor @param language language of the APIs object (string) @param forPreparation flag indicating this object is just needed for a preparation process (boolean) @param parent reference to the parent object (QObject) """ QObject.__init__(self, parent) self.setObjectName("APIs_%s" % language) self.__inPreparation = False self.__language = language self.__forPreparation = forPreparation self.__lexer = Lexers.getLexer(self.__language) self.__apifiles = Preferences.getEditorAPI(self.__language) self.__apifiles.sort() if self.__lexer is None: self.__apis = None else: self.__apis = QsciAPIs(self.__lexer) self.connect(self.__apis, SIGNAL("apiPreparationFinished()"), self.__apiPreparationFinished) self.connect(self.__apis, SIGNAL("apiPreparationCancelled()"), self.__apiPreparationCancelled) self.connect(self.__apis, SIGNAL("apiPreparationStarted()"), self.__apiPreparationStarted) self.__loadAPIs()
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.setDefaultFont(QFont('Mono', 10, 0, False)) # self.lexer.setDefaultColor(Qt.darkGray) 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.api.load(os.path.dirname(__file__) + "/api/PyQGIS_1.8.api") # self.api.load(os.path.dirname(__file__) + "/api/osgeo_gdal-ogr_1.9.1-1.api") # self.api.load("qgis.networkanalysis.api") # self.api.load("qgis.gui.api") # self.api.load("qgis.core.api") # self.api.load("qgis.analysis.api") # self.api.prepare() # self.lexer.setAPIs(self.api) self.setLexer(self.lexer)
def setLexers(self, lexer): from qgis.core import QgsApplication 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( QgsApplication.pkgDataPath() + "/python/qsci_apis/pyqgis_master.pap" ) self.setLexer(self.lexer)
def init(self): self.setCaretLineBackgroundColor(self.colorStyle.caret) self.setMarginsBackgroundColor(self.colorStyle.margin) self.setMarkerBackgroundColor(self.colorStyle.marker,self.ARROW_MARKER_NUM) self.font = QFont() self.font.setFamily(fontName) #self.font.setFixedPitch(True) self.font.setPointSize(self.fontSize) self.setFont(self.font) self.fontmetrics = QFontMetrics(self.font) self.setMarginsFont(self.font) # Margin 0 is used for line numbers #self.setMarginLineNumbers(0, True) #self.setMarginWidth(0, self.fontmetrics.width("0000") + 6) self.setMarginLineNumbers(1, True) self.setMarginWidth(1, QString("-------")) self.setCaretLineVisible(True) if self.lang == 0: self.lexer = QsciLexerPython() elif self.lang == 1: self.lexer = QsciLexerCPP() elif self.lang == 2: self.lexer = LexerSquirrel(self.colorStyle,self) self.lexer.setDefaultFont(self.font) self.api = QsciAPIs(self.lexer) self.api.load(ospathjoin(apiDir,"emo.api")) self.api.prepare() self.lexer.setAPIs(self.api) #Very important do not change line otherwise gg self.setLexer(self.lexer) #Very important do not change line otherwise gg
def setup_api(self): """Load and prepare Python API""" if self.lexer() is None: return self.api = QsciAPIs(self.lexer()) is_api_ready = False api_path = CONF.get('editor', 'api') if not osp.isfile(api_path): from spyderlib.config import DATA_PATH api_path = osp.join(DATA_PATH, 'python.api') if osp.isfile(api_path): CONF.set('editor', 'api', api_path) else: return False api_size = CONF.get('editor', 'api_size', None) current_api_size = os.stat(api_path).st_size if api_size is not None and api_size == current_api_size: if self.api.isPrepared(): is_api_ready = self.api.loadPrepared() else: CONF.set('editor', 'api_size', current_api_size) if not is_api_ready: if self.api.load(api_path): self.api.prepare() self.connect(self.api, SIGNAL("apiPreparationFinished()"), self.api.savePrepared) return is_api_ready
class CSSEditor(BaseEditor): def __init__(self, parent=None, line_num_margin=3, autocomplete_list=None): super(CSSEditor, self).__init__(parent, line_num_margin, autocomplete_list) # Set HTML lexer self.lexer = QsciLexerCSS(self) self.lexer.setDefaultFont(self.editor_font) # Set auto-completion self.api = QsciAPIs(self.lexer) if autocomplete_list is not None: # Add additional completion strings for i in autocomplete_list: self.api.add(i) self.api.prepare() self.setAutoCompletionThreshold(3) self.setAutoCompletionSource(QsciScintilla.AcsAPIs) self.setLexer(self.lexer)
def setApi(self, text): self.api = QsciAPIs(self.lexer) self.api.load(ospathjoin(apiDir,text+".api")) self.api.prepare() self.lexer.setAPIs(self.api) #Very important do not change line otherwise gg self.setLexer(self.lexer) #Very important do not change line otherwise gg editStyle = config.readStyle() self.setMarginsBackgroundColor(QColor(editStyle["margin"])) '''This is done cause the margin color is set only when lexer is set
class HTMLEditor(BaseEditor): def __init__(self, parent=None, line_num_margin=3, autocomplete_list=None): super(HTMLEditor, self).__init__(parent, line_num_margin, autocomplete_list) # Set HTML lexer self.lexer = QsciLexerHTML(self) self.lexer.setDefaultFont(self.editor_font) self.lexer.setFont(self.editor_font, QsciLexerHTML.Default) # Text between tags self.lexer.setFont(self.editor_font, QsciLexerHTML.JavaScriptWord) # Text between script tags # Set auto-completion self.api = QsciAPIs(self.lexer) if autocomplete_list is not None: # Add additional completion strings for i in autocomplete_list: self.api.add(i) self.api.prepare() self.setAutoCompletionThreshold(3) self.setAutoCompletionSource(QsciScintilla.AcsAPIs) self.setAutoCompletionUseSingle(QsciScintilla.AcusExplicit) self.setLexer(self.lexer)
def __init__(self, parent=None): QsciScintilla.__init__(self, parent) self.setTabWidth(4) self.setTabIndents(True) self.setIndentationsUseTabs(False) self._lexer = QsciLexerPython() self._lexer.setFont(QFont('DejaVu Sans Mono')) self._lexer.setIndentationWarning(QsciLexerPython.Tabs) # load current preview to lexer api = QsciAPIs(self._lexer) api.load('/tmp/preview.py') api.prepare() self.setLexer(self._lexer) self.setAutoCompletionSource(QsciScintilla.AcsAll) self.setAutoCompletionThreshold(2) self.setAutoIndent(True) self.setCaretForegroundColor(g.cursor_color) self.zoomTo(5)
def initLexer(self): if self.lexerType == self.LEXER_PYTHON: 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) elif self.lexerType == self.LEXER_R: # R lexer self.lexer = LexerR() self.setLexer(self.lexer)
class PrepareAPIs(QObject): def __init__(self, api_lexer, api_files, pap_file): QObject.__init__(self) self._api = None self._api_files = api_files self._api_lexer = api_lexer self._pap_file = pap_file def _clearLexer(self): self.qlexer = None def _stopPreparation(self): if self._api is not None: self._api.cancelPreparation() self._api = None sys.exit(1) def _preparationFinished(self): self._clearLexer() try: if os.path.exists(self._pap_file): os.remove(self._pap_file) prepd = self._api.savePrepared(unicode(self._pap_file)) self._api = None sys.exit(0 if prepd else 1) except Exception as err: self._api = None sys.exit(1) def prepareAPI(self): try: self._api = QsciAPIs(self._api_lexer) self._api.apiPreparationFinished.connect(self._preparationFinished) for api_file in self._api_files: self._api.load(unicode(api_file)) self._api.prepare() except Exception as err: self._api = None sys.exit(1)
def setup_api(self): """Load and prepare API""" if self.lexer() is None: return self.api = QsciAPIs(self.lexer()) is_api_ready = False api_path = CONF.get('editor', 'api') if not os.path.isfile(api_path): return False api_stat = CONF.get('editor', 'api_stat', None) current_api_stat = os.stat(api_path) if (api_stat is not None) and (api_stat == current_api_stat): if self.api.isPrepared(): is_api_ready = self.api.loadPrepared() else: CONF.set('editor', 'api_stat', current_api_stat) if not is_api_ready: if self.api.load(api_path): self.api.prepare() self.connect(self.api, SIGNAL("apiPreparationFinished()"), self.api.savePrepared) return is_api_ready
def prepareAPI(self): # self.ui.textEdit_Qsci.setLexer(0) exec u'self.qlexer = {0}(self.ui.textEdit_Qsci)'.format( self._api_lexer) # self.ui.textEdit_Qsci.setLexer(self.qlexer) self._api = QsciAPIs(self.qlexer) self._api.apiPreparationFinished.connect(self._preparationFinished) for api_file in self._api_files: self._api.load(unicode(api_file)) try: self._api.prepare() except Exception, err: self._api = None self._clearLexer() self.ui.label.setText( QCoreApplication.translate("PythonConsole", "Error preparing file...")) self.ui.progressBar.setVisible(False) self.ui.plainTextEdit.setVisible(True) self.ui.plainTextEdit.insertPlainText(err) self.ui.buttonBox.button(QDialogButtonBox.Cancel).setText( self.trUtf8("Done")) self.adjustSize()
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)
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.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, type=bool) chekBoxPreparedAPI = self.settings.value("pythonConsole/usePreparedAPIFile", False, type=bool) if chekBoxAPI: apisdir = os.path.join(QgsApplication.pkgDataPath(), "python", "qsci_apis") pap = os.path.join(apisdir, "pyqgis.pap") mpap = os.path.join(apisdir, "pyqgis-master.pap") if os.path.exists(mpap): # override installed with master .pap build pap = mpap if QgsApplication.isRunningFromBuildDir(): bdir = os.path.dirname(QgsApplication.buildOutputPath()) bpap = os.path.join(bdir, "python", "qsci_apis", "pyqgis-master.pap") if os.path.exists(bpap): # if not generated .pap exists, else fall back to preprepared one pap = bpap 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)
def prepareAPI(self): # self.ui.textEdit_Qsci.setLexer(0) exec(u"self.qlexer = {0}(self.ui.textEdit_Qsci)".format(self._api_lexer)) # self.ui.textEdit_Qsci.setLexer(self.qlexer) self._api = QsciAPIs(self.qlexer) self._api.apiPreparationFinished.connect(self._preparationFinished) for api_file in self._api_files: self._api.load(unicode(api_file)) try: self._api.prepare() except Exception as err: self._api = None self._clearLexer() self.ui.label.setText(QCoreApplication.translate("PythonConsole", "Error preparing file...")) self.ui.progressBar.setVisible(False) self.ui.plainTextEdit.setVisible(True) self.ui.plainTextEdit.insertPlainText(err) self.ui.buttonBox.button(QDialogButtonBox.Cancel).setText(self.trUtf8("Done")) self.adjustSize()
def __init__(self, parent=None): super(CommandShell, self).__init__(parent) self.setMinimumHeight(50) self.setMaximumHeight(50) self.settings = QSettings() self.lex = QsciLexerPython(self) self.apis = QsciAPIs(self.lex) self.lex.setAPIs(self.apis) self.setLexers() self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.SendScintilla(QsciScintilla.SCI_SETVSCROLLBAR, 0) self.setFolding(0) self._start_prompt = _start_prompt self.prompt = self._start_prompt self.currentfunction = None self.setAutoCompletionSource(self.AcsAPIs) self.setAutoCompletionThreshold(1) self.setAutoCompletionReplaceWord(True) self.setCallTipsStyle(QsciScintilla.CallTipsNoContext) self.parent().installEventFilter(self) self.textChanged.connect(self.text_changed) self._lastcompletions = None
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.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, 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)
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)
def initCompleter(self): dictionary = None if self.db: dictionary = self.db.connector.getSqlDictionary() if not dictionary: # use the generic sql dictionary from .sql_dictionary import getSqlDictionary dictionary = getSqlDictionary() wordlist = [] for name, value in dictionary.iteritems(): wordlist += value # concat lists wordlist = list(set(wordlist)) # remove duplicates api = QsciAPIs(self.editSql.lexer()) for word in wordlist: api.add(word) api.prepare() self.editSql.lexer().setAPIs(api)
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 QsciEditor(TextEditBaseWidget): """ QScintilla Base Editor Widget """ LEXERS = { ('py', 'pyw', 'python'): (QsciLexerPython, '#'), ('f', 'for'): (QsciLexerFortran77, 'c'), ('f90', 'f95', 'f2k'): (QsciLexerFortran, '!'), ('diff', 'patch', 'rej'): (QsciLexerDiff, ''), 'css': (QsciLexerCSS, '#'), ('htm', 'html'): (QsciLexerHTML, ''), ('c', 'cpp', 'h'): (QsciLexerCPP, '//'), ('bat', 'cmd', 'nt'): (QsciLexerBatch, 'rem '), ('properties', 'session', 'ini', 'inf', 'reg', 'url', 'cfg', 'cnf', 'aut', 'iss'): (QsciLexerProperties, '#'), } TAB_ALWAYS_INDENTS = ('py', 'pyw', 'python', 'c', 'cpp', 'h') OCCURENCE_INDICATOR = QsciScintilla.INDIC_CONTAINER EOL_MODES = {"\r\n": QsciScintilla.EolWindows, "\n": QsciScintilla.EolUnix, "\r": QsciScintilla.EolMac} def __init__(self, parent=None): TextEditBaseWidget.__init__(self, parent) # Indicate occurences of the selected word self.connect(self, SIGNAL('cursorPositionChanged(int, int)'), self.__cursor_position_changed) self.__find_start = None self.__find_end = None self.__find_flags = None self.SendScintilla(QsciScintilla.SCI_INDICSETSTYLE, self.OCCURENCE_INDICATOR, QsciScintilla.INDIC_BOX) self.SendScintilla(QsciScintilla.SCI_INDICSETFORE, self.OCCURENCE_INDICATOR, 0x4400FF) self.supported_language = None self.comment_string = None # Mark errors, warnings, ... self.markers = [] self.marker_lines = {} self.error = self.markerDefine(QPixmap(get_image_path('error.png'), 'png')) self.warning = self.markerDefine(QPixmap(get_image_path('warning.png'), 'png')) # Scintilla Python API self.api = None # Mark occurences timer self.occurences_timer = QTimer(self) self.occurences_timer.setSingleShot(True) self.occurences_timer.setInterval(750) self.connect(self.occurences_timer, SIGNAL("timeout()"), self.__mark_occurences) # Context menu self.setup_context_menu() # Tab key behavior self.tab_indents = None self.tab_mode = True # see QsciEditor.set_tab_mode def setup_editor(self, linenumbers=True, language=None, code_analysis=False, code_folding=False, show_eol_chars=False, show_whitespace=False, font=None, wrap=True, tab_mode=True): # Lexer self.set_language(language) # Tab always indents (even when cursor is not at the begin of line) self.tab_indents = language in self.TAB_ALWAYS_INDENTS self.tab_mode = tab_mode if linenumbers: self.connect( self, SIGNAL('linesChanged()'), self.__lines_changed ) self.setup_margins(linenumbers, code_analysis, code_folding) if font is not None: self.set_font(font) # Re-enable brace matching (already enabled in TextEditBaseWidget.setup # but for an unknown reason, changing the 'set_font' call above reset # this setting to default, which is no brace matching): # XXX: find out why self.setBraceMatching(QsciScintilla.SloppyBraceMatch) self.setMatchedBraceBackgroundColor(Qt.yellow) self.set_eol_chars_visible(show_eol_chars) self.set_whitespace_visible(show_whitespace) self.toggle_wrap_mode(wrap) self.setup_api() self.setModified(False) def set_tab_mode(self, enable): """ enabled = tab always indent (otherwise tab indents only when cursor is at the beginning of a line) """ self.tab_mode = enable def set_language(self, language): self.supported_language = False self.comment_string = '' if language is not None: for key in self.LEXERS: if language.lower() in key: self.supported_language = True lexer_class, comment_string = self.LEXERS[key] self.comment_string = comment_string if lexer_class is not None: # Fortran lexers are sometimes unavailable: # the corresponding class is then replaced by None # (see the import lines at the beginning of the script) self.setLexer( lexer_class(self) ) break def is_python(self): return isinstance(self.lexer(), QsciLexerPython) #=============================================================================== # QScintilla #=============================================================================== def setup(self): """Reimplement TextEditBaseWidget method""" TextEditBaseWidget.setup(self) # Wrapping if CONF.get('editor', 'wrapflag'): self.setWrapVisualFlags(QsciScintilla.WrapFlagByBorder) # Indentation self.setIndentationGuides(True) self.setIndentationGuidesForegroundColor(Qt.lightGray) # 80-columns edge self.setEdgeColumn(80) self.setEdgeMode(QsciScintilla.EdgeLine) # Auto-completion self.setAutoCompletionSource(QsciScintilla.AcsAll) def setup_margins(self, linenumbers=True, code_analysis=False, code_folding=False): """ Setup margin settings (except font, now set in self.set_font) """ for i_margin in range(5): # Reset margin settings self.setMarginWidth(i_margin, 0) self.setMarginLineNumbers(i_margin, False) self.setMarginMarkerMask(i_margin, 0) self.setMarginSensitivity(i_margin, False) if linenumbers: # 1: Line numbers margin self.setMarginLineNumbers(1, True) self.update_line_numbers_margin() if code_analysis: # 2: Errors/warnings margin mask = (1 << self.error) | (1 << self.warning) self.setMarginSensitivity(0, True) self.setMarginMarkerMask(0, mask) self.setMarginWidth(0, 14) self.connect(self, SIGNAL('marginClicked(int,int,Qt::KeyboardModifiers)'), self.__margin_clicked) if code_folding: # 0: Folding margin self.setMarginWidth(2, 14) self.setFolding(QsciScintilla.BoxedFoldStyle) # Colors fcol = CONF.get('scintilla', 'margins/foregroundcolor') bcol = CONF.get('scintilla', 'margins/backgroundcolor') if fcol: self.setMarginsForegroundColor(QColor(fcol)) if bcol: self.setMarginsBackgroundColor(QColor(bcol)) fcol = CONF.get('scintilla', 'foldmarginpattern/foregroundcolor') bcol = CONF.get('scintilla', 'foldmarginpattern/backgroundcolor') if fcol and bcol: self.setFoldMarginColors(QColor(fcol), QColor(bcol)) def setup_api(self): """Load and prepare API""" if self.lexer() is None: return self.api = QsciAPIs(self.lexer()) is_api_ready = False api_path = CONF.get('editor', 'api') if not os.path.isfile(api_path): return False api_stat = CONF.get('editor', 'api_stat', None) current_api_stat = os.stat(api_path) if (api_stat is not None) and (api_stat == current_api_stat): if self.api.isPrepared(): is_api_ready = self.api.loadPrepared() else: CONF.set('editor', 'api_stat', current_api_stat) if not is_api_ready: if self.api.load(api_path): self.api.prepare() self.connect(self.api, SIGNAL("apiPreparationFinished()"), self.api.savePrepared) return is_api_ready def set_whitespace_visible(self, state): """Show/hide whitespace""" if state: self.setWhitespaceVisibility(QsciScintilla.WsVisible) else: self.setWhitespaceVisibility(QsciScintilla.WsInvisible) def set_eol_chars_visible(self, state): """Show/hide EOL characters""" self.setEolVisibility(state) def convert_eol_chars(self): """Convert EOL characters to current mode""" self.convertEols(self.eolMode()) def remove_trailing_spaces(self): """Remove trailing spaces""" text_before = unicode(self.text()) text_after = sourcecode.remove_trailing_spaces(text_before) if text_before != text_after: self.setText(text_after) def fix_indentation(self): """Replace tabs by spaces""" text_before = unicode(self.text()) text_after = sourcecode.fix_indentation(text_before) if text_before != text_after: self.setText(text_after) def set_eol_mode(self, text): """ Set QScintilla widget EOL mode based on *text* EOL characters """ if isinstance(text, QString): text = unicode(text) eol_chars = sourcecode.get_eol_chars(text) if eol_chars is not None: self.setEolMode(self.EOL_MODES[eol_chars]) def get_line_separator(self): """Return line separator based on current EOL mode""" current_mode = self.eolMode() for eol_chars, mode in self.EOL_MODES.iteritems(): if current_mode == mode: return eol_chars else: return '' def __find_first(self, text): """Find first occurence""" self.__find_flags = QsciScintilla.SCFIND_MATCHCASE | \ QsciScintilla.SCFIND_WHOLEWORD self.__find_start = 0 line = self.lines()-1 self.__find_end = self.position_from_lineindex(line, self.text(line).length()) return self.__find_next(text) def __find_next(self, text): """Find next occurence""" if self.__find_start == self.__find_end: return False self.SendScintilla(QsciScintilla.SCI_SETTARGETSTART, self.__find_start) self.SendScintilla(QsciScintilla.SCI_SETTARGETEND, self.__find_end) self.SendScintilla(QsciScintilla.SCI_SETSEARCHFLAGS, self.__find_flags) pos = self.SendScintilla(QsciScintilla.SCI_SEARCHINTARGET, len(text), text) if pos == -1: return False self.__find_start = self.SendScintilla(QsciScintilla.SCI_GETTARGETEND) return True def __get_found_occurence(self): """Return found occurence""" spos = self.SendScintilla(QsciScintilla.SCI_GETTARGETSTART) epos = self.SendScintilla(QsciScintilla.SCI_GETTARGETEND) return (spos, epos - spos) def __cursor_position_changed(self): """Cursor position has changed""" #TODO: Add attribute for occurences marking enable/disable: # if self.occurences_marking: self.occurences_timer.stop() self.occurences_timer.start() def __mark_occurences(self): """Marking occurences of the currently selected word""" self.SendScintilla(QsciScintilla.SCI_SETINDICATORCURRENT, self.OCCURENCE_INDICATOR) self.SendScintilla(QsciScintilla.SCI_INDICATORCLEARRANGE, 0, self.length()) if not self.supported_language: return text = self.get_current_word() if text.isEmpty(): return # Highlighting all occurences of word *text* ok = self.__find_first(text) while ok: spos = self.SendScintilla(QsciScintilla.SCI_GETTARGETSTART) epos = self.SendScintilla(QsciScintilla.SCI_GETTARGETEND) self.SendScintilla(QsciScintilla.SCI_INDICATORFILLRANGE, spos, epos-spos) ok = self.__find_next(text) def __lines_changed(self): """Update margin""" self.update_line_numbers_margin() def update_line_numbers_margin(self): """Update margin width""" width = log(self.lines(), 10) + 2 self.setMarginWidth(1, QString('0'*int(width))) def delete(self): """Remove selected text""" # Used by global callbacks in Spyder -> delete_action QsciScintilla.removeSelectedText(self) def set_font(self, font): """Set shell font""" if self.lexer() is None: self.setFont(font) else: self.lexer().setFont(font) self.setLexer(self.lexer()) self.setMarginsFont(font) def set_text(self, text): """Set the text of the editor""" self.setText(text) self.set_eol_mode(text) def get_text(self): """Return editor text""" return self.text() def paste(self): """ Reimplement QsciScintilla's method to fix the following issue: on Windows, pasted text has only 'LF' EOL chars even if the original text has 'CRLF' EOL chars """ clipboard = QApplication.clipboard() text = unicode(clipboard.text()) if len(text.splitlines()) > 1: eol_chars = self.get_line_separator() clipboard.setText( eol_chars.join((text+eol_chars).splitlines()) ) # Standard paste TextEditBaseWidget.paste(self) def fold_header(self, line): """Is it a fold header line?""" lvl = self.SendScintilla(QsciScintilla.SCI_GETFOLDLEVEL, line) return lvl & QsciScintilla.SC_FOLDLEVELHEADERFLAG def fold_expanded(self, line): """Is fold expanded?""" return self.SendScintilla(QsciScintilla.SCI_GETFOLDEXPANDED, line) def get_folded_lines(self): """Return the list of folded line numbers""" return [line for line in xrange(self.lines()) \ if self.fold_header(line) and not self.fold_expanded(line) ] def unfold_all(self): """Unfold all folded lines""" for line in self.get_folded_lines(): self.foldLine(line) #=============================================================================== # High-level editor features #=============================================================================== def highlight_line(self, line): """Highlight line number *line*""" text = unicode(self.text(line-1)).rstrip() self.setSelection(line-1, len(text), line-1, 0) self.ensureLineVisible(line-1) def cleanup_code_analysis(self): """Remove all code analysis markers""" for marker in self.markers: self.markerDeleteHandle(marker) self.markers = [] self.marker_lines = {} def process_code_analysis(self, check_results): """Analyze filename code with pyflakes""" self.cleanup_code_analysis() if check_results is None: # Not able to compile module return for message, line0, error in check_results: line1 = line0 - 1 marker = self.markerAdd(line1, 0 if error else 1) self.markers.append(marker) if line1 not in self.marker_lines: self.marker_lines[line1] = [] self.marker_lines[line1].append( (message, error) ) def __highlight_warning(self, line): self.highlight_line(line+1) self.__show_code_analysis_results(line) def go_to_next_warning(self): """Go to next code analysis warning message""" cline, _ = self.getCursorPosition() lines = sorted(self.marker_lines.keys()) for line in lines: if line > cline: self.__highlight_warning(line) return else: self.__highlight_warning(lines[0]) def go_to_previous_warning(self): """Go to previous code analysis warning message""" cline, _ = self.getCursorPosition() lines = sorted(self.marker_lines.keys(), reverse=True) for line in lines: if line < cline: self.__highlight_warning(line) return else: self.__highlight_warning(lines[0]) def __show_code_analysis_results(self, line): """Show warning/error messages""" if line in self.marker_lines: msglist = [ msg for msg, _error in self.marker_lines[line] ] self.show_calltip(self.tr("Code analysis"), msglist, color='#129625', at_line=line) def __margin_clicked(self, margin, line, modifier): """Margin was clicked, that's for sure!""" if margin == 0: self.__show_code_analysis_results(line) def mouseMoveEvent(self, event): line = self.get_line_number_at(event.pos()) self.__show_code_analysis_results(line) QsciScintilla.mouseMoveEvent(self, event) def add_prefix(self, prefix): """Add prefix to current line or selected line(s)""" if self.hasSelectedText(): # Add prefix to selected line(s) line_from, index_from, line_to, index_to = self.getSelection() if index_to == 0: line_to -= 1 self.beginUndoAction() for line in range(line_from, line_to+1): self.insertAt(prefix, line, 0) self.endUndoAction() if index_to == 0: line_to += 1 else: index_to += len(prefix) self.setSelection(line_from, index_from+len(prefix), line_to, index_to) else: # Add prefix to current line line, index = self.getCursorPosition() self.beginUndoAction() self.insertAt(prefix, line, 0) self.endUndoAction() self.setCursorPosition(line, index+len(prefix)) def remove_prefix(self, prefix): """Remove prefix from current line or selected line(s)""" if self.hasSelectedText(): # Remove prefix from selected line(s) line_from, index_from, line_to, index_to = self.getSelection() if index_to == 0: line_to -= 1 self.beginUndoAction() for line in range(line_from, line_to+1): if not self.text(line).startsWith(prefix): continue self.setSelection(line, 0, line, len(prefix)) self.removeSelectedText() if line == line_from: index_from = max([0, index_from-len(prefix)]) if line == line_to and index_to != 0: index_to = max([0, index_to-len(prefix)]) if index_to == 0: line_to += 1 self.setSelection(line_from, index_from, line_to, index_to) self.endUndoAction() else: # Remove prefix from current line line, index = self.getCursorPosition() if not self.text(line).startsWith(prefix): return self.beginUndoAction() self.setSelection(line, 0, line, len(prefix)) self.removeSelectedText() self.setCursorPosition(line, index-len(prefix)) self.endUndoAction() self.setCursorPosition(line, max([0, index-len(prefix)])) def fix_indent(self, forward=True): """ Fix indentation (Python only, no text selection) forward=True: fix indent only if text is not enough indented (otherwise force indent) forward=False: fix indent only if text is too much indented (otherwise force unindent) """ if not self.is_python(): return line, index = self.getCursorPosition() prevtext = unicode(self.text(line-1)).rstrip() indent = self.indentation(line) correct_indent = self.indentation(line-1) if prevtext.endswith(':'): # Indent correct_indent += 4 elif prevtext.endswith('continue') or prevtext.endswith('break'): # Unindent correct_indent -= 4 elif prevtext.endswith(','): rlmap = {")":"(", "]":"[", "}":"{"} for par in rlmap: i_right = prevtext.rfind(par) if i_right != -1: prevtext = prevtext[:i_right] i_left = prevtext.rfind(rlmap[par]) if i_left != -1: prevtext = prevtext[:i_left] else: break else: prevexpr = re.split(r'\(|\{|\[', prevtext)[-1] correct_indent = len(prevtext)-len(prevexpr) if forward: if indent == correct_indent or indent > correct_indent: # Force indent correct_indent = indent + 4 elif indent == correct_indent or indent < correct_indent: # Force unindent correct_indent = indent - 4 if correct_indent >= 0: self.beginUndoAction() self.setSelection(line, 0, line, indent) self.removeSelectedText() if index > indent: index -= indent-correct_indent else: index = correct_indent self.insertAt(" "*correct_indent, line, 0) self.setCursorPosition(line, index) self.endUndoAction() def __no_char_before_cursor(self): line, index = self.getCursorPosition() self.setSelection(line, 0, line, index) selected_text = unicode(self.selectedText()) self.clear_selection() return len(selected_text.strip()) == 0 def indent(self): """Indent current line or selection""" if self.hasSelectedText(): self.add_prefix( " "*4 ) elif self.__no_char_before_cursor() or \ (self.tab_indents and self.tab_mode): if self.is_python(): self.fix_indent(forward=True) else: self.add_prefix( " "*4 ) else: self.SendScintilla(QsciScintilla.SCI_TAB) def unindent(self): """Unindent current line or selection""" if self.hasSelectedText(): self.remove_prefix( " "*4 ) elif self.__no_char_before_cursor() or \ (self.tab_indents and self.tab_mode): if self.is_python(): self.fix_indent(forward=False) else: self.remove_prefix( " "*4 ) def comment(self): """Comment current line or selection""" self.add_prefix(self.comment_string) def uncomment(self): """Uncomment current line or selection""" self.remove_prefix(self.comment_string) def blockcomment(self): """Block comment current line or selection""" comline = self.comment_string + '='*(80-len(self.comment_string)) \ + self.get_line_separator() if self.hasSelectedText(): line_from, _index_from, line_to, _index_to = self.getSelection() lines = range(line_from, line_to+1) else: line, _index = self.getCursorPosition() lines = [line] self.beginUndoAction() self.insertAt( comline, lines[-1]+1, 0 ) self.insertAt( comline, lines[0], 0 ) for l in lines: self.insertAt( '# ', l+1, 0 ) self.endUndoAction() self.setCursorPosition(lines[-1]+2, 80) def __is_comment_bar(self, line): comline = '#' + '='*79 + self.get_line_separator() self.setSelection(line, 0, line+1, 0) return unicode(self.selectedText()) == comline def unblockcomment(self): """Un-block comment current line or selection""" line, index = self.getCursorPosition() self.setSelection(line, 0, line, 1) if unicode(self.selectedText()) != '#': self.setCursorPosition(line, index) return # Finding first comment bar line1 = line-1 while line1 >= 0 and not self.__is_comment_bar(line1): line1 -= 1 if not self.__is_comment_bar(line1): self.setCursorPosition(line, index) return # Finding second comment bar line2 = line+1 while line2 < self.lines() and not self.__is_comment_bar(line2): line2 += 1 if not self.__is_comment_bar(line2) or line2 > self.lines()-2: self.setCursorPosition(line, index) return lines = range(line1+1, line2) self.beginUndoAction() self.setSelection(line2, 0, line2+1, 0) self.removeSelectedText() for l in lines: self.setSelection(l, 0, l, 2) self.removeSelectedText() self.setSelection(line1, 0, line1+1, 0) self.removeSelectedText() self.endUndoAction() #=============================================================================== # Qt Event handlers #=============================================================================== def setup_context_menu(self): """Setup context menu""" self.undo_action = create_action(self, translate("SimpleEditor", "Undo"), shortcut=keybinding('Undo'), icon=get_icon('undo.png'), triggered=self.undo) self.redo_action = create_action(self, translate("SimpleEditor", "Redo"), shortcut=keybinding('Redo'), icon=get_icon('redo.png'), triggered=self.redo) self.cut_action = create_action(self, translate("SimpleEditor", "Cut"), shortcut=keybinding('Cut'), icon=get_icon('editcut.png'), triggered=self.cut) self.copy_action = create_action(self, translate("SimpleEditor", "Copy"), shortcut=keybinding('Copy'), icon=get_icon('editcopy.png'), triggered=self.copy) paste_action = create_action(self, translate("SimpleEditor", "Paste"), shortcut=keybinding('Paste'), icon=get_icon('editpaste.png'), triggered=self.paste) self.delete_action = create_action(self, translate("SimpleEditor", "Delete"), shortcut=keybinding('Delete'), icon=get_icon('editdelete.png'), triggered=self.removeSelectedText) selectall_action = create_action(self, translate("SimpleEditor", "Select all"), shortcut=keybinding('SelectAll'), icon=get_icon('selectall.png'), triggered=self.selectAll) self.menu = QMenu(self) add_actions(self.menu, (self.undo_action, self.redo_action, None, self.cut_action, self.copy_action, paste_action, self.delete_action, None, selectall_action)) # Read-only context-menu self.readonly_menu = QMenu(self) add_actions(self.readonly_menu, (self.copy_action, None, selectall_action)) def keyPressEvent(self, event): """Reimplement Qt method""" key = event.key() ctrl = event.modifiers() & Qt.ControlModifier shift = event.modifiers() & Qt.ShiftModifier # Zoom in/out if ((key == Qt.Key_Plus) and ctrl) \ or ((key==Qt.Key_Equal) and shift and ctrl): self.zoomIn() event.accept() elif (key == Qt.Key_Minus) and ctrl: self.zoomOut() event.accept() # Indent/unindent elif key == Qt.Key_Backtab: self.unindent() event.accept() elif (key == Qt.Key_Tab): if self.is_completion_widget_visible(): self.SendScintilla(QsciScintilla.SCI_TAB) else: self.indent() event.accept() elif (key == Qt.Key_V) and ctrl: self.paste() event.accept() #TODO: find other shortcuts... # elif (key == Qt.Key_3) and ctrl: # self.comment() # event.accept() # elif (key == Qt.Key_2) and ctrl: # self.uncomment() # event.accept() # elif (key == Qt.Key_4) and ctrl: # self.blockcomment() # event.accept() # elif (key == Qt.Key_5) and ctrl: # self.unblockcomment() # event.accept() else: QsciScintilla.keyPressEvent(self, event) def mousePressEvent(self, event): """Reimplement Qt method""" if event.button() == Qt.MidButton: self.setFocus() event = QMouseEvent(QEvent.MouseButtonPress, event.pos(), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) QsciScintilla.mousePressEvent(self, event) QsciScintilla.mouseReleaseEvent(self, event) self.paste() else: QsciScintilla.mousePressEvent(self, event) def contextMenuEvent(self, event): """Reimplement Qt method""" state = self.hasSelectedText() self.copy_action.setEnabled(state) self.cut_action.setEnabled(state) self.delete_action.setEnabled(state) self.undo_action.setEnabled( self.isUndoAvailable() ) self.redo_action.setEnabled( self.isRedoAvailable() ) menu = self.menu if self.isReadOnly(): menu = self.readonly_menu menu.popup(event.globalPos()) event.accept()
def __init__(self, parent=None): #CONSTRUCTOR DE LA CLASE HEREDADA super(editor, self).__init__(parent) #DEFINICION DE LA FUENTE Y SUS PROPIEDADES font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) self.lineas_marcadas = [] #PROFIEDADES AVANZADAS DE LA FUENTE DEL EDITOR fontmetrics = QFontMetrics(font) self.setMarginsFont(font) #SE CAMBIA EL MARGEN ANCHO DE LA FUNETE self.setMarginWidth(0, fontmetrics.width("00000") - 15) #MARGEN DE LOS NUMEROS DE LINEA self.setMarginLineNumbers(0, True) #COLOR DE FONDO DE LOS NUMEROS DE LINEA self.setMarginsBackgroundColor(QColor("#E0E0E0")) self.setMarginSensitivity(1, True) #CREAMOS LA SEÑA PARA AGREGAR LINEA MARCADA self.connect(self, SIGNAL('marginClicked(int, int, Qt::KeyboardModifiers)'), self.on_margin_clicked) #SE DEFINE EL ICONO A MOSTRAR EN LA LINEA MARCADA self.markerDefine(QsciScintilla.Circle, self.ARROW_MARKER_NUM) self.setMarkerBackgroundColor(QColor("#FF6C3B"), self.ARROW_MARKER_NUM) #RESALTADO DE PARENTECIS,CORCHETES Y OTROS self.setBraceMatching(QsciScintilla.SloppyBraceMatch) #RESALTADO DE LA LINEA DONDE SE ENCUENTRA EL CURSOR self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor("#32f24c")) #AUTOIDENTACION self.setAutoIndent(True) self.setIndentationGuides(True) self.setIndentationsUseTabs(True) self.setIndentationWidth(4) #DEFINIMOS EL RESALTADO O LEXER self.lexer = QsciLexerPascal() self.lexer.setDefaultFont(font) #FUENTE DEL LEXER #API PARA EL AUTOCOMPETADO api = QsciAPIs(self.lexer) self.palabraAutocompletar(api) api.prepare() self.cambiarColores() self.setLexer(self.lexer) self.setAutoCompletionThreshold(1) self.setAutoCompletionSource(QsciScintilla.AcsAPIs) #ESCONDER SCROLLBAR HORIZONTAL self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) #TAMAÑO MINIMO DEL EDITOR self.setMinimumSize(600, 360)
class PythonEditor(BaseEditor): def __init__(self, parent=None, line_num_margin=3, autocomplete_list=None): super(PythonEditor, self).__init__(parent, line_num_margin, autocomplete_list) # Set Python lexer self.lexer = QsciLexerPython(self) self.lexer.setDefaultFont(self.editor_font) self.lexer.setFont(self.editor_font, QsciLexerPython.Comment) # Indentation warning ("The indentation is inconsistent when compared to the previous line") self.lexer.setIndentationWarning(QsciLexerPython.Inconsistent) # Set auto-completion self.api = QsciAPIs(self.lexer) if autocomplete_list is not None: # Add additional completion strings for i in autocomplete_list: self.api.add(i) self.api.prepare() self.setAutoCompletionThreshold(3) self.setAutoCompletionSource(QsciScintilla.AcsAll) self.setAutoCompletionUseSingle(QsciScintilla.AcusExplicit) self.setLexer(self.lexer) # PEP8 tabs self.setIndentationsUseTabs(False) self.setAutoIndent(True) self.setIndentationGuides(True) # PEP8 edge column line self.edgecol = 80 # Linters self.linter = 'internal' def clean_code(self): self.setText(autopep8.fix_code(self.text(), options={'aggressive': 2})) def check_code(self, path): self._clear_all_margin_markers() self.lint_data = {} try: warnings = linter.check(self.text(), os.path.basename(path)) for w in warnings: w.type = 'warning' key = w.lineno - 1 if key in self.lint_data.keys(): self.lint_data[key].append(w) else: self.lint_data[key] = [w] self._add_warn_margin_marker(w.lineno - 1) except linter.LinterSyntaxError as e: e.type = 'error' self.lint_data[e.lineno - 1] = e self._add_error_margin_marker(e.lineno - 1) except linter.LinterUnexpectedError as e: print(e) def margin_clicked(self, marnum, linenum, modifiers): if self._margin_popup.isVisible(): self._hide_margin_popup() else: try: warnings = self.lint_data[linenum] desc = '' if isinstance(warnings, linter.LinterError): desc = warnings.message self._show_margin_popup(desc.strip(), bg_color=self.colorErrorBackground) else: for warning in warnings: desc += warning.message % warning.message_args + '\n' self._show_margin_popup(desc.strip(), bg_color=self.colorWarnBackground) except KeyError: pass return True
editor.setCaretLineVisible(True) editor.setCaretLineBackgroundColor(QtGui.QColor("#CDA869")) ## Margins colors # line numbers margin editor.setMarginsBackgroundColor(QtGui.QColor("#333333")) editor.setMarginsForegroundColor(QtGui.QColor("#CCCCCC")) # folding margin colors (foreground,background) editor.setFoldMarginColors(QtGui.QColor("#99CC66"), QtGui.QColor("#333300")) ## Choose a lexer lexer = QsciLexerPython1() lexer.setDefaultFont(font) editor.setLexer(lexer) myApis = QsciAPIs(lexer) myApis.add(QtCore.QString("SDR")) myApis.add(QtCore.QString("sdr")) myApis.prepare() editor.setAutoCompletionSource(QsciScintilla.AcsAll) editor.setAutoCompletionCaseSensitivity(False) editor.setAutoCompletionThreshold(1) ## Render on screen editor.show() ## Show this file in the editor editor.setText(open("chardet.txt").read()) sys.exit(app.exec_())
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) 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 PrepareAPIDialog(QDialog): def __init__(self, api_lexer, api_files, pap_file, parent=None): QDialog.__init__(self, parent) self.ui = Ui_APIsDialogPythonConsole() self.ui.setupUi(self) self.setWindowTitle( QCoreApplication.translate("PythonConsole", "Compile APIs")) self.ui.plainTextEdit.setVisible(False) self.ui.textEdit_Qsci.setVisible(False) self.adjustSize() self._api = None self.ui.buttonBox.rejected.connect(self._stopPreparation) self._api_files = api_files self._api_lexer = api_lexer self._pap_file = pap_file def _clearLexer(self): # self.ui.textEdit_Qsci.setLexer(0) self.qlexer = None def _stopPreparation(self): if self._api is not None: self._api.cancelPreparation() self._api = None self._clearLexer() self.close() def _preparationFinished(self): self._clearLexer() if os.path.exists(self._pap_file): os.remove(self._pap_file) self.ui.label.setText( QCoreApplication.translate("PythonConsole", "Saving prepared file...")) prepd = self._api.savePrepared(unicode(self._pap_file)) rslt = self.trUtf8("Error") if prepd: rslt = QCoreApplication.translate("PythonConsole", "Saved") self.ui.label.setText(u'{0} {1}'.format(self.ui.label.text(), rslt)) self._api = None self.ui.progressBar.setVisible(False) self.ui.buttonBox.button(QDialogButtonBox.Cancel).setText( QCoreApplication.translate("PythonConsole", "Done")) self.adjustSize() def prepareAPI(self): # self.ui.textEdit_Qsci.setLexer(0) exec u'self.qlexer = {0}(self.ui.textEdit_Qsci)'.format( self._api_lexer) # self.ui.textEdit_Qsci.setLexer(self.qlexer) self._api = QsciAPIs(self.qlexer) self._api.apiPreparationFinished.connect(self._preparationFinished) for api_file in self._api_files: self._api.load(unicode(api_file)) try: self._api.prepare() except Exception, err: self._api = None self._clearLexer() self.ui.label.setText( QCoreApplication.translate("PythonConsole", "Error preparing file...")) self.ui.progressBar.setVisible(False) self.ui.plainTextEdit.setVisible(True) self.ui.plainTextEdit.insertPlainText(err) self.ui.buttonBox.button(QDialogButtonBox.Cancel).setText( self.trUtf8("Done")) self.adjustSize()
def __init__(self, parent=None, fileName=None, readOnlyFiles=[]): ''' Constructor ''' super(CppEditor, self).__init__(parent) self.parent = parent self.roFiles = readOnlyFiles self.setAcceptDrops(False) # drag&drop is on its parent # Set the default font font = QtGui.QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # C/C++ lexer self.lexer = QsciLexerCPP(self, True) self.lexer.setDefaultFont(font) self.libraryAPIs = QsciAPIs(QsciLexerCPP(self, True)) self.setLexer(self.lexer) self.SendScintilla(QsciScintilla.SCI_STYLESETFONT, 1, 'Courier') # Auto-indent self.setTabWidth(4) #self.setIndentationsUseTabs(False) self.setAutoIndent(True) # Current line visible with special background color self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QtGui.QColor("#ffe4e4")) # Enable brace matching self.setBraceMatching(QsciScintilla.SloppyBraceMatch) # Enable folding visual- use boxes self.setFolding(QsciScintilla.BoxedTreeFoldStyle) # show line numbers fontmetrics = QtGui.QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(0, fontmetrics.width("00000") + 4) self.setMarginLineNumbers(0, True) self.setMarginsBackgroundColor(QtGui.QColor("#ccccee")) # not too small self.setMinimumSize(400, 200) # set the length of the string before the editor tries to auto-complete self.setAutoCompletionThreshold(3) # tell the editor we are using a QsciAPI for the auto-completion self.setAutoCompletionSource(QsciScintilla.AcsAPIs) # removed remaining right side characters from the current cursor self.setAutoCompletionReplaceWord(True) # "CTRL+Space" autocomplete self.shortcut_ctrl_space = QtGui.QShortcut( QtGui.QKeySequence("Ctrl+Space"), self) self.connect(self.shortcut_ctrl_space, QtCore.SIGNAL('activated()'), self.autoCompleteFromAll) if fileName: self.curFile = fileName self.loadFile(fileName) self.isUntitled = False else: self.curFile = PROJECT_NONAME + USER_CODE_EXT self.setText(__default_content__) self.isUntitled = True self.updateApiKeywords() self.isModified = False self.connect(self, QtCore.SIGNAL('textChanged()'), self.onTextChanged)
class CppEditor(QsciScintilla): ''' classdocs ''' def __init__(self, parent=None, fileName=None, readOnlyFiles=[]): ''' Constructor ''' super(CppEditor, self).__init__(parent) self.parent = parent self.roFiles = readOnlyFiles self.setAcceptDrops(False) # drag&drop is on its parent # Set the default font font = QtGui.QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # C/C++ lexer self.lexer = QsciLexerCPP(self, True) self.lexer.setDefaultFont(font) self.libraryAPIs = QsciAPIs(QsciLexerCPP(self, True)) self.setLexer(self.lexer) self.SendScintilla(QsciScintilla.SCI_STYLESETFONT, 1, 'Courier') # Auto-indent self.setTabWidth(4) #self.setIndentationsUseTabs(False) self.setAutoIndent(True) # Current line visible with special background color self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QtGui.QColor("#ffe4e4")) # Enable brace matching self.setBraceMatching(QsciScintilla.SloppyBraceMatch) # Enable folding visual- use boxes self.setFolding(QsciScintilla.BoxedTreeFoldStyle) # show line numbers fontmetrics = QtGui.QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(0, fontmetrics.width("00000") + 4) self.setMarginLineNumbers(0, True) self.setMarginsBackgroundColor(QtGui.QColor("#ccccee")) # not too small self.setMinimumSize(400, 200) # set the length of the string before the editor tries to auto-complete self.setAutoCompletionThreshold(3) # tell the editor we are using a QsciAPI for the auto-completion self.setAutoCompletionSource(QsciScintilla.AcsAPIs) # removed remaining right side characters from the current cursor self.setAutoCompletionReplaceWord(True) # "CTRL+Space" autocomplete self.shortcut_ctrl_space = QtGui.QShortcut( QtGui.QKeySequence("Ctrl+Space"), self) self.connect(self.shortcut_ctrl_space, QtCore.SIGNAL('activated()'), self.autoCompleteFromAll) if fileName: self.curFile = fileName self.loadFile(fileName) self.isUntitled = False else: self.curFile = PROJECT_NONAME + USER_CODE_EXT self.setText(__default_content__) self.isUntitled = True self.updateApiKeywords() self.isModified = False self.connect(self, QtCore.SIGNAL('textChanged()'), self.onTextChanged) def onTextChanged(self): self.isModified = True self.parent.onChildContentChanged() def loadFile(self, fileName): qfile = QtCore.QFile(fileName) if not qfile.open(QtCore.QFile.ReadOnly | QtCore.QFile.Text): QtGui.QMessageBox.warning( self, PROJECT_ALIAS, "Cannot read file %s:\n%s." % (fileName, qfile.errorString())) return False ret = True try: # workaround for OS X QFile.readAll() f = open(qfile.fileName(), 'r') self.clear() for line in f.readlines(): self.append(line) except: QtGui.QMessageBox.warning(self, PROJECT_ALIAS, "failed to read %s." % fileName) ret = False finally: f.close() qfile.close() return ret def saveFile(self, fileName): if str(fileName).find(' ') >= 0: QtGui.QMessageBox.warning( self, PROJECT_ALIAS, 'File path "%s" contains space(s). Please save to a valid location.' % fileName) return None qfile = QtCore.QFile(fileName) if not qfile.open(QtCore.QFile.WriteOnly | QtCore.QFile.Text): QtGui.QMessageBox.warning( self, PROJECT_ALIAS, "Cannot write file %s:\n%s." % (fileName, qfile.errorString())) return None try: qfile.writeData(self.text()) except: QtGui.QMessageBox.warning(self, PROJECT_ALIAS, "Failed to save %s." % fileName) qfile.close() return None qfile.close() self.curFile = fileName self.isUntitled = False self.isModified = False return fileName def saveAs(self): fileName = QtGui.QFileDialog.getSaveFileName( self, "Save As", self.curFile, PROJECT_ALIAS + " (*" + USER_CODE_EXT + ");;" + "C source (*.c);;C++ source (*.cpp);;Text File (*.txt);;All files (*.*)" ) if not fileName: return None return self.saveFile(fileName) def save(self): f1 = os.path.abspath(self.curFile) for fname in self.roFiles: if f1 == os.path.abspath(fname): # same file if QtGui.QMessageBox.question(self.parent, "Project is read-only", "This project is marked as \"read-only\".\n" + \ "Please click \"Cancel\" and save this to another location.\n\n" + \ "Continue saving the current project anyway?", "OK", "Cancel"): return None #return self.saveAs() return self.saveFile(self.curFile) if self.isUntitled: return self.saveAs() else: return self.saveFile(self.curFile) def currentFile(self): return self.curFile def modified(self): return self.isModified def updateApiKeywords(self): self.libraryAPIs.clear() self.apiKeywords = self.parent.getDefaultKeywords() headerfiles = [] for line in range(self.lines()): txt = str(self.text(line)).strip() if txt.find('int') == 0: # e.g. reached "int main()" break elif txt.find('#include') == 0: txt = ''.join(txt.split()) temp = txt[len('#includes') - 1:] header = temp[1:-1] # get the header file hfile = os.path.join('libraries', header[:-2], header) if os.path.isfile(hfile): if not (hfile in headerfiles): headerfiles.append(hfile) if len(headerfiles): #print 'parsing: ', headerfiles self.apiKeywords += getLibraryKeywords(headerfiles) #self.apiKeywords = list(set(self.apiKeywords)) # remove duplicates for keyword in self.apiKeywords: self.libraryAPIs.add(keyword) self.libraryAPIs.prepare() self.lexer.setAPIs(self.libraryAPIs) def insertIncludeDirective(self, library=''): directive = '#include <' + library + '.h>\r\n' insert_pos = 0 found_inc = False for line in range(self.lines()): txt = str(self.text(line)).strip() if txt.find('int') == 0: # e.g. reached "int main()" insert_pos = line - 1 break elif txt.find('#include') == 0: found_inc = True elif found_inc: insert_pos = line break if insert_pos < 0 or insert_pos >= self.lines(): insert_pos = 0 self.insertAt(directive, insert_pos, 0) self.updateApiKeywords()
class ScriptEdit(QsciScintilla): LEXER_PYTHON = 0 LEXER_R = 1 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 font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(20) self.setFont(font) self.setMarginsFont(font) 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')) # Folding self.setFolding(QsciScintilla.BoxedTreeFoldStyle) self.setFoldMarginColors(QColor('#d3d7cf'), 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.AcsAPIs) self.setFonts(10) def setFonts(self, size): # Load font from Python console settings settings = QSettings() fontName = settings.value('pythonConsole/fontfamilytext', 'Monospace') fontSize = int(settings.value('pythonConsole/fontsize', size)) 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) 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): if self.lexerType == self.LEXER_PYTHON: 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) elif self.lexerType == self.LEXER_R: # R lexer self.lexer = LexerR() self.setLexer(self.lexer)
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 Editor(base.Base): # Braces BRACE = {'(': ')', '[': ']'} # Quotes QUOTE = {"'": "'", '"': '"'} # Marcadores MARKER_MODIFIED = 3 MARKER_SAVED = 4 # Indicadores WORD_INDICATOR = 0 WARNING_INDICATOR = 1 def __init__(self, obj_file): super(Editor, self).__init__() self.obj_file = obj_file # Asociación con el objeto EdisFile self._font = None # Configuración use_tabs = settings.get_setting('editor/usetabs') self.setIndentationsUseTabs(use_tabs) self.setAutoIndent(settings.get_setting('editor/indent')) self.setBackspaceUnindents(True) # Quita el scrollbar self.send("sci_sethscrollbar", 0) # Configuración de indicadores self.send("sci_indicsetstyle", Editor.WORD_INDICATOR, "indic_box") self.send("sci_indicsetfore", Editor.WORD_INDICATOR, QColor("#FF0000")) self.send("sci_indicsetstyle", Editor.WARNING_INDICATOR, "indic_squiggle") self.send("sci_indicsetfore", Editor.WARNING_INDICATOR, QColor("#0000FF")) # Scheme self.scheme = editor_scheme.get_scheme( settings.get_setting('editor/scheme')) # Folding self.setFolding(QsciScintilla.PlainFoldStyle) # en márgen 2 self.setMarginWidth(3, 5) # 5px de espacios en márgen 3 self.send("sci_markersetfore", QsciScintilla.SC_MARKNUM_FOLDER, QColor(self.scheme['FoldMarkerFore'])) self.send("sci_markersetback", QsciScintilla.SC_MARKNUM_FOLDER, QColor(self.scheme['FoldMarkerBack'])) self.setFoldMarginColors(QColor(self.scheme['FoldMarginBack']), QColor(self.scheme['FoldMarginFore'])) # Marcadores self.markerDefine(QsciScintilla.SC_MARK_LEFTRECT, Editor.MARKER_MODIFIED) self.setMarkerBackgroundColor(QColor(226, 255, 141), Editor.MARKER_MODIFIED) self.markerDefine(QsciScintilla.SC_MARK_LEFTRECT, Editor.MARKER_SAVED) self.setMarkerBackgroundColor(QColor(55, 142, 103), Editor.MARKER_SAVED) # Actualiza flags (espacios en blanco, cursor, sidebar, etc) self.update_options() # Lexer self._lexer = lexer.Lexer() self.setLexer(self._lexer) # Autocompletado self.api = None if settings.get_setting('editor/completion'): self.active_code_completion() # Indentación self._indentation = settings.get_setting('editor/width-indent') self.send("sci_settabwidth", self._indentation) # Minimapa self.minimap = None if settings.get_setting('editor/minimap'): self.minimap = minimap.Minimap(self) self.connect(self, SIGNAL("textChanged()"), self.minimap.update_code) # Thread que busca palabras self.search_thread = editor_helpers.SearchThread() self.connect(self.search_thread, SIGNAL("foundWords(PyQt_PyObject)"), self.mark_words) # Analizador de estilo de código self.checker = None if settings.get_setting('editor/style-checker'): self.load_checker() # Fuente font = settings.get_setting('editor/font') font_size = settings.get_setting('editor/size-font') self.load_font(font, font_size) self.setMarginsBackgroundColor(QColor(self.scheme['SidebarBack'])) self.setMarginsForegroundColor(QColor(self.scheme['SidebarFore'])) # Línea actual self.send("sci_setcaretlinevisible", settings.get_setting('editor/show-caret-line')) self.send("sci_setcaretlineback", QColor(self.scheme['CaretLineBack'])) self.send("sci_setcaretfore", QColor(self.scheme['CaretLineFore'])) self.send("sci_setcaretlinebackalpha", self.scheme['CaretLineAlpha']) # Cursor caret_period = settings.get_setting('editor/cursor-period') self.send("sci_setcaretperiod", caret_period) # Márgen if settings.get_setting('editor/show-margin'): self.update_margin() # Brace matching self.setBraceMatching(int(settings.get_setting('editor/match-brace'))) self.setMatchedBraceBackgroundColor( QColor(self.scheme['MatchedBraceBack'])) self.setMatchedBraceForegroundColor( QColor(self.scheme['MatchedBraceFore'])) self.setUnmatchedBraceBackgroundColor( QColor(self.scheme['UnmatchedBraceBack'])) self.setUnmatchedBraceForegroundColor( QColor(self.scheme['UnmatchedBraceFore'])) # Selecciones Múltiples self.send("sci_setadditionalcaretfore", QColor(157, 64, 40)) self.send("sci_setadditionalcaretsblink", 1) self.send("sci_setadditionalselalpha", 100) self.send("sci_setmultipleselection", 1) self.send("sci_setadditionalselectiontyping", 1) # Conexiones self.connect(self, SIGNAL("linesChanged()"), self.update_sidebar) self.connect(self, SIGNAL("textChanged()"), self._add_marker_modified) @property def filename(self): return self.obj_file.filename @property def is_modified(self): return self.isModified() def load_font(self, fuente, tam): self._font = QFont(fuente, tam) if self._lexer is None: self.setFont(self._font) else: self._lexer.setFont(self._font) self.setMarginsFont(self._font) def load_checker(self, activated=True): if activated and self.checker is not None: return if not activated: self.checker = None self.clear_indicators(Editor.WARNING_INDICATOR) else: self.checker = checker.Checker(self) self.connect(self.checker, SIGNAL("finished()"), self._show_violations) def set_brace_matching(self): match_brace = int(settings.get_setting('editor/match-brace')) self.setBraceMatching(match_brace) def update_options(self): """ Actualiza las opciones del editor """ if settings.get_setting('editor/show-tabs-spaces'): self.setWhitespaceVisibility(self.WsVisible) else: self.setWhitespaceVisibility(self.WsInvisible) self.setIndentationGuides(settings.get_setting('editor/show-guides')) if settings.get_setting('editor/wrap-mode'): self.setWrapMode(self.WrapWord) else: self.setWrapMode(self.WrapNone) self.send("sci_setcaretstyle", settings.get_setting('editor/cursor')) self.setCaretWidth(settings.get_setting('editor/caret-width')) self.setAutoIndent(settings.get_setting('editor/indent')) self.send("sci_setcaretperiod", settings.get_setting('editor/cursor-period')) current_line = settings.get_setting('editor/show-caret-line') self.send("sci_setcaretlinevisible", current_line) self.setEolVisibility(settings.get_setting('editor/eof')) def update_sidebar(self): """ Ajusta el ancho del sidebar """ fmetrics = QFontMetrics(self._font) lines = str(self.lines()) + '0' line_number = settings.get_setting('editor/show-line-number') width = fmetrics.width(lines) if line_number else 0 self.setMarginWidth(0, width) def show_line_numbers(self): line_number = settings.get_setting('editor/show-line-number') self.setMarginLineNumbers(0, line_number) self.update_sidebar() def update_margin(self): """ Actualiza el ancho del márgen de línea """ if settings.get_setting('editor/show-margin'): self.setEdgeMode(QsciScintilla.EdgeLine) ancho = settings.get_setting('editor/width-margin') self.setEdgeColumn(ancho) self.setEdgeColor(QColor(self.scheme['Margin'])) else: self.setEdgeMode(QsciScintilla.EdgeNone) def update_indentation(self): ancho = settings.get_setting('editor/width-indent') self.send("sci_settabwidth", ancho) self._indentation = ancho def mark_words(self, palabras): self.clear_indicators(Editor.WORD_INDICATOR) for p in palabras: self.fillIndicatorRange(p[0], p[1], p[0], p[2], Editor.WORD_INDICATOR) def active_code_completion(self, enabled=True): if self.api is not None and enabled: return if enabled: self.api = QsciAPIs(self._lexer) if settings.get_setting('editor/completion-keywords'): for keyword in keywords.keywords: self.api.add(keyword) self.api.prepare() source = QsciScintilla.AcsAPIs if settings.get_setting('editor/completion-document'): source = QsciScintilla.AcsAll elif settings.get_setting('editor/completion-document'): source = QsciScintilla.AcsDocument else: source = QsciScintilla.AcsNone threshold = settings.get_setting('editor/completion-threshold') self.setAutoCompletionThreshold(threshold) self.setAutoCompletionSource(source) cs = settings.get_setting('editor/completion-cs') self.setAutoCompletionCaseSensitivity(cs) repl_word = settings.get_setting('editor/completion-replace-word') self.setAutoCompletionReplaceWord(repl_word) show_single = settings.get_setting('editor/completion-single') use_single = 2 if show_single else 0 self.setAutoCompletionUseSingle(use_single) else: self.api = None self.setAutoCompletionSource(0) def _add_marker_modified(self): """ Agrega el marcador cuando el texto cambia """ if not settings.get_setting('editor/mark-change'): return nline, _ = self.getCursorPosition() if self.markersAtLine(nline): self.markerDelete(nline) self.markerAdd(nline, Editor.MARKER_MODIFIED) def _show_violations(self): data = self.checker.data self.clear_indicators(Editor.WARNING_INDICATOR) for line, message in list(data.items()): line = int(line) - 1 self.fillIndicatorRange(line, 0, line, self.lineLength(line), Editor.WARNING_INDICATOR) def _text_under_cursor(self): """ Texto seleccionado con el cursor """ line, index = self.getCursorPosition() # Posición del cursor word = self.wordAtLineIndex(line, index) # Palabra en esa pos return word def mouseReleaseEvent(self, e): super(Editor, self).mouseReleaseEvent(e) if e.button() == Qt.LeftButton: word = self._text_under_cursor() if not word: self.clear_indicators(Editor.WORD_INDICATOR) return self.search_thread.find(word, self.text()) def mouseMoveEvent(self, event): super(Editor, self).mouseMoveEvent(event) if self.checker is None: return position = event.pos() line = str(self.lineAt(position) + 1) message = self.checker.data.get(line, None) if message is not None: QToolTip.showText(self.mapToGlobal(position), message) def keyReleaseEvent(self, event): super(Editor, self).keyReleaseEvent(event) line, _ = self.getCursorPosition() self.emit(SIGNAL("linesChanged(int)"), line) def keyPressEvent(self, event): super(Editor, self).keyPressEvent(event) key = event.key() # Borra indicador de palabra if key == Qt.Key_Escape: self.clear_indicators(Editor.WORD_INDICATOR) return # Completado de comillas if key == Qt.Key_Apostrophe: if settings.get_setting('editor/complete-single-quote'): self._complete_quote(event) return if key == Qt.Key_QuoteDbl: if settings.get_setting('editor/complete-double-quote'): self._complete_quote(event) return # Completado de llaves if key == Qt.Key_BraceLeft: if settings.get_setting('editor/complete-brace'): self._complete_key(event) if key in (Qt.Key_BracketLeft, Qt.Key_ParenLeft, Qt.Key_BracketRight, Qt.Key_ParenRight): self._complete_brace(event) def _complete_quote(self, event): """ Autocompleta comillas simples y dobles, si existe el complementario el cursor se mueve un espacio. """ complementary = Editor.QUOTE.get(event.text()) line, index = self.getCursorPosition() text_line = self.text(line) portion = text_line[index - 1:index + 1] if portion in settings.QUOTES: self.setSelection(line, index, line, index + 1) self.replaceSelectedText('') else: self.insertAt(complementary, line, index) def _complete_brace(self, event): """ Autocompleta un brace cuando es abierto '(, ['. Si existe el complementario '), ]' el índice del cursor se mueve un espacio. """ brace_close = settings.BRACES.get(event.text(), None) braces_open = list(Editor.BRACE.keys()) line, index = self.getCursorPosition() if event.text() in settings.BRACES.values(): text_line = self.text(line) found = 0 # Busco un brace cerrado en el texto for token in text_line: if token in settings.BRACES.values(): found += 1 try: # Brace abierto brace_open = text_line[index] except: brace_open = '' if found > 1 and brace_open not in braces_open: # Reemplazo el brace sobrante por un string vacío self.setSelection(line, index, line, index + 1) self.replaceSelectedText('') return elif event.text() in settings.BRACES.keys( ) and brace_close is not None: # Inserto el brace complementario self.insertAt(brace_close, line, index) def _complete_key(self, event): """ Autocompleta llaves en funciones y estructuras. Si se usa typedef en la definición de la estructura se completa la variable. """ line, index = self.getCursorPosition() text_line = self.text(line) # Estructura if REGEX_STRUCT.match(text_line): if text_line.split()[0] == 'typedef': # Obtengo la variable split_line = text_line.split() if split_line[-1] == '{': typedef = split_line[-2] else: typedef = split_line[-1][:-1] text = "}%s;" % typedef.title() else: text = "};" self.insert("\n") self.insertAt(text, line + 1, 0) return # Función portion = text_line[index - 3:index] if ')' in portion: self.insert("\n") current_indentation = self.indentation(line) self.setIndentation(line + 1, current_indentation) self.insertAt('}', line + 1, current_indentation) def resizeEvent(self, e): super(Editor, self).resizeEvent(e) if self.minimap is not None: self.minimap.update_geometry() def comment(self): # FIXME: tener en cuenta /* */ # FIXME: no funciona si el comentario no inicia en el índice 0 if self.hasSelectedText(): line_from, _, line_to, _ = self.getSelection() # Iterar todas las líneas seleccionadas self.send("sci_beginundoaction") for line in range(line_from, line_to + 1): self.insertAt('//', line, 0) self.send("sci_endundoaction") else: line, _ = self.getCursorPosition() self.insertAt('//', line, 0) def uncomment(self): if self.hasSelectedText(): line_from, _, line_to, _ = self.getSelection() self.send("sci_beginundoaction") for line in range(line_from, line_to + 1): self.setSelection(line, 0, line, 2) if not self.text(line).startswith('//'): continue self.removeSelectedText() self.send("sci_endundoaction") else: line, _ = self.getCursorPosition() if not self.text(line).startswith('//'): return self.setSelection(line, 0, line, 2) self.removeSelectedText() def to_lowercase(self): if self.hasSelectedText(): text = self.selectedText().lower() else: line, _ = self.getCursorPosition() text = self.text(line).lower() self.setSelection(line, 0, line, len(text)) self.replaceSelectedText(text) def to_uppercase(self): if self.hasSelectedText(): text = self.selectedText().upper() else: line, _ = self.getCursorPosition() text = self.text(line).upper() self.setSelection(line, 0, line, len(text)) self.replaceSelectedText(text) def to_title(self): if self.hasSelectedText(): text = self.selectedText().title() else: line, _ = self.getCursorPosition() text = self.text(line).title() self.setSelection(line, 0, line, len(text)) self.replaceSelectedText(text) def duplicate_line(self): self.send("sci_lineduplicate") def delete_line(self): if self.hasSelectedText(): self.send("sci_beginundoaction") desde, desde_indice, hasta, _ = self.getSelection() self.setCursorPosition(desde, desde_indice) while desde != hasta: self.send("sci_linedelete") desde += 1 self.send("sci_endundoaction") else: self.send("sci_linedelete") def indent_more(self): if self.hasSelectedText(): self.send("sci_beginundoaction") desde, _, hasta, _ = self.getSelection() for linea in range(desde, hasta + 1): self.indent(linea) self.send("sci_endundoaction") else: linea, _ = self.getCursorPosition() self.indent(linea) def indent_less(self): if self.hasSelectedText(): self.send("sci_beginundoaction") desde, _, hasta, _ = self.getSelection() for linea in range(desde, hasta + 1): self.unindent(linea) self.send("sci_endundoaction") else: linea, _ = self.getCursorPosition() self.unindent(linea) def move_down(self): self.send("sci_moveselectedlinesdown") def move_up(self): self.send("sci_moveselectedlinesup") def reemplazar_tabs_por_espacios(self): codigo = self.text() codigo_sin_tabs = codigo.replace('\t', ' ' * self._indentation) self.setText(codigo_sin_tabs) def saved(self): # Esta señal sirve para actualizar el árbol de símbolos self.emit(SIGNAL("fileSaved(QString)"), self.obj_file.filename) self.setModified(False) # Itera todas las líneas y si existe un _marker_modified agrega # un _marker_save for nline in range(self.lines()): if self.markersAtLine(nline): self.markerAdd(nline, Editor.MARKER_SAVED) def dropEvent(self, event): self.emit(SIGNAL("dropEvent(PyQt_PyObject)"), event)