Esempio n. 1
0
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')
Esempio n. 2
0
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')
Esempio n. 3
0
class Editor(QsciScintilla):
    def __init__(self, parent=None):
        super(Editor,self).__init__(parent)
        self.parent = parent

        self.settings = QSettings()

        # Enable non-ascii chars for editor
        self.setUtf8(True)

        # Set the default font
        font = QFont()
        font.setFamily('Courier')
        font.setFixedPitch(True)
        font.setPointSize(10)
        self.setFont(font)
        self.setMarginsFont(font)
        # Margin 0 is used for line numbers
        #fm = QFontMetrics(font)
        #fontmetrics = QFontMetrics(font)
        self.setMarginsFont(font)
        self.setMarginWidth(1, "00000")
        self.setMarginLineNumbers(1, True)
        self.setMarginsForegroundColor(QColor("#3E3EE3"))
        self.setMarginsBackgroundColor(QColor("#f9f9f9"))
        self.setCaretLineVisible(True)
        self.setCaretLineBackgroundColor(QColor("#fcf3ed"))

        # Clickable margin 1 for showing markers
#        self.setMarginSensitivity(1, True)
#        self.connect(self,
#            SIGNAL('marginClicked(int, int, Qt::KeyboardModifiers)'),
#            self.on_margin_clicked)
#        self.markerDefine(QsciScintilla.RightArrow,
#            self.ARROW_MARKER_NUM)
#        self.setMarkerBackgroundColor(QColor("#ee1111"),
#            self.ARROW_MARKER_NUM)

        self.setMinimumHeight(120)
        #self.setMinimumWidth(300)

        # Folding
        self.setFolding(QsciScintilla.PlainFoldStyle)
        self.setFoldMarginColors(QColor("#f4f4f4"),QColor("#f4f4f4"))
        #self.setWrapMode(QsciScintilla.WrapCharacter)

        ## Edge Mode
        self.setEdgeMode(QsciScintilla.EdgeLine)
        self.setEdgeColumn(80)
        self.setEdgeColor(QColor("#FF0000"))

        #self.setWrapMode(QsciScintilla.WrapCharacter)
        self.setWhitespaceVisibility(QsciScintilla.WsVisibleAfterIndent)
        #self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0)
        
        self.settingsEditor()
        
        # Annotations
        #self.setAnnotationDisplay(QsciScintilla.ANNOTATION_BOXED)
        
        # Indentation
        self.setAutoIndent(True)
        self.setIndentationsUseTabs(False)
        self.setIndentationWidth(4)
        self.setTabIndents(True)
        self.setBackspaceUnindents(True)
        self.setTabWidth(4)
        self.setIndentationGuides(True)

        ## Disable command key
        ctrl, shift = self.SCMOD_CTRL<<16, self.SCMOD_SHIFT<<16
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L')+ ctrl)
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T')+ ctrl)
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D')+ ctrl)
        #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Z')+ ctrl)
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Y')+ ctrl)
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L')+ ctrl+shift)

        ## New QShortcut = ctrl+space/ctrl+alt+space for Autocomplete
        self.newShortcutCS = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Space), self)
        self.newShortcutCS.setContext(Qt.WidgetShortcut)
        self.newShortcutCS.activated.connect(self.autoCompleteKeyBinding)
        self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self)
        self.runScut.setContext(Qt.WidgetShortcut)
        self.runScut.activated.connect(self.runSelectedCode)
        self.runScriptScut = QShortcut(QKeySequence(Qt.SHIFT + Qt.CTRL + Qt.Key_E), self)
        self.runScriptScut.setContext(Qt.WidgetShortcut)
        self.runScriptScut.activated.connect(self.runScriptCode)
        
        self.commentScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_3), self)
        self.commentScut.setContext(Qt.WidgetShortcut)
        self.commentScut.activated.connect(self.parent.pc.commentCode)
        self.uncommentScut = QShortcut(QKeySequence(Qt.SHIFT + Qt.CTRL + Qt.Key_3), self)
        self.uncommentScut.setContext(Qt.WidgetShortcut)
        self.uncommentScut.activated.connect(self.parent.pc.uncommentCode)
        
    def settingsEditor(self):
        # Set Python lexer
        self.setLexers()
        threshold = self.settings.value("pythonConsole/autoCompThresholdEditor", 2).toInt()[0]
        radioButtonSource = self.settings.value("pythonConsole/autoCompleteSourceEditor", 'fromAPI').toString()
        autoCompEnabled = self.settings.value("pythonConsole/autoCompleteEnabledEditor", True).toBool()
        self.setAutoCompletionThreshold(threshold)
        if autoCompEnabled:
            if radioButtonSource == 'fromDoc':
                self.setAutoCompletionSource(self.AcsDocument)
            elif radioButtonSource == 'fromAPI':
                self.setAutoCompletionSource(self.AcsAPIs)
            elif radioButtonSource == 'fromDocAPI':
                self.setAutoCompletionSource(self.AcsAll)
        else:
            self.setAutoCompletionSource(self.AcsNone)

    def autoCompleteKeyBinding(self):
        radioButtonSource = self.settings.value("pythonConsole/autoCompleteSourceEditor").toString()
        autoCompEnabled = self.settings.value("pythonConsole/autoCompleteEnabledEditor").toBool()
        if autoCompEnabled:
            if radioButtonSource == 'fromDoc':
                self.autoCompleteFromDocument()
            elif radioButtonSource == 'fromAPI':
                self.autoCompleteFromAPIs()
            elif radioButtonSource == 'fromDocAPI':
                self.autoCompleteFromAll()

    def on_margin_clicked(self, nmargin, nline, modifiers):
        # Toggle marker for the line the margin was clicked on
        if self.markersAtLine(nline) != 0:
            self.markerDelete(nline, self.ARROW_MARKER_NUM)
        else:
            self.markerAdd(nline, self.ARROW_MARKER_NUM)

    def setLexers(self):
        from qgis.core import QgsApplication

        self.lexer = QsciLexerPython()
        self.lexer.setIndentationWarning(QsciLexerPython.Inconsistent)
        self.lexer.setFoldComments(True)
        self.lexer.setFoldQuotes(True)

        loadFont = self.settings.value("pythonConsole/fontfamilytextEditor", "Monospace").toString()
        fontSize = self.settings.value("pythonConsole/fontsizeEditor", 10).toInt()[0]

        font = QFont(loadFont)
        font.setFixedPitch(True)
        font.setPointSize(fontSize)
        font.setStyleHint(QFont.TypeWriter)
        font.setStretch(QFont.SemiCondensed)
        font.setLetterSpacing(QFont.PercentageSpacing, 87.0)
        font.setBold(False)

        self.lexer.setDefaultFont(font)
        self.lexer.setColor(Qt.red, 1)
        self.lexer.setColor(Qt.darkGreen, 5)
        self.lexer.setColor(Qt.darkBlue, 15)
        self.lexer.setFont(font, 1)
        self.lexer.setFont(font, 3)
        self.lexer.setFont(font, 4)

        self.api = QsciAPIs(self.lexer)
        chekBoxAPI = self.settings.value("pythonConsole/preloadAPI", True).toBool()
        if chekBoxAPI:
            self.api.loadPrepared( QgsApplication.pkgDataPath() + "/python/qsci_apis/pyqgis_master.pap" )
        else:
            apiPath = self.settings.value("pythonConsole/userAPI").toStringList()
            for i in range(0, len(apiPath)):
                self.api.load(QString(unicode(apiPath[i])))
            self.api.prepare()
            self.lexer.setAPIs(self.api)

        self.setLexer(self.lexer)

    def move_cursor_to_end(self):
        """Move cursor to end of text"""
        line, index = self.get_end_pos()
        self.setCursorPosition(line, index)
        self.ensureCursorVisible()
        self.ensureLineVisible(line)

    def get_end_pos(self):
        """Return (line, index) position of the last character"""
        line = self.lines() - 1
        return (line, self.text(line).length())

    def contextMenuEvent(self, e):
        menu = QMenu(self)
        iconRun = QgsApplication.getThemeIcon("console/iconRunConsole.png")
        iconCodePad = QgsApplication.getThemeIcon("console/iconCodepadConsole.png")
        iconNewEditor = QgsApplication.getThemeIcon("console/iconTabEditorConsole.png")
        iconCommentEditor = QgsApplication.getThemeIcon("console/iconCommentEditorConsole.png")
        iconUncommentEditor = QgsApplication.getThemeIcon("console/iconUncommentEditorConsole.png")
        iconSettings = QgsApplication.getThemeIcon("console/iconSettingsConsole.png")
        hideEditorAction = menu.addAction("Hide Editor",
                                     self.hideEditor)
        menu.addSeparator()
        newTabAction = menu.addAction(iconNewEditor,
                                    "New Tab",
                                    self.parent.newTab, 'Ctrl+T')
        closeTabAction = menu.addAction("Close Tab",
                                    self.parent.close, 'Ctrl+W')
        menu.addSeparator()
        runSelected = menu.addAction(iconRun,
                                   "Enter selected",
                                   self.runSelectedCode, 'Ctrl+E')
        runScript = menu.addAction(iconRun,
                                   "Run Script",
                                   self.runScriptCode, 'Shift+Ctrl+E')
        menu.addSeparator()
        undoAction = menu.addAction("Undo", self.undo, QKeySequence.Undo)
        redoAction = menu.addAction("Redo", self.redo, QKeySequence.Redo)
        menu.addSeparator()
        cutAction = menu.addAction("Cut",
                                    self.cut,
                                    QKeySequence.Cut)
        copyAction = menu.addAction("Copy",
                                    self.copy,
                                    QKeySequence.Copy)
        pasteAction = menu.addAction("Paste", self.paste, QKeySequence.Paste)
        menu.addSeparator()
        commentCodeAction = menu.addAction(iconCommentEditor, "Comment",
                                           self.parent.pc.commentCode, 'Ctrl+3')
        uncommentCodeAction = menu.addAction(iconUncommentEditor, "Uncomment", 
                                             self.parent.pc.uncommentCode, 
                                             'Shift+Ctrl+3')
        menu.addSeparator()
        codePadAction = menu.addAction(iconCodePad,
                                        "Share on codepad",
                                        self.codepad)
        menu.addSeparator()
        showCodeInspection = menu.addAction("Hide/Show Object list",
                                     self.objectListEditor)
        menu.addSeparator()
        selectAllAction = menu.addAction("Select All",
                                         self.selectAll,
                                         QKeySequence.SelectAll)
        menu.addSeparator()
        settingsDialog = menu.addAction(iconSettings,
                                        "Settings",
                                        self.parent.pc.openSettings)
        pasteAction.setEnabled(False)
        codePadAction.setEnabled(False)
        cutAction.setEnabled(False)
        runSelected.setEnabled(False)
        copyAction.setEnabled(False)
        selectAllAction.setEnabled(False)
        closeTabAction.setEnabled(False)
        undoAction.setEnabled(False)
        redoAction.setEnabled(False)
        if self.parent.mw.count() > 1:
            closeTabAction.setEnabled(True)
        if self.hasSelectedText():
            runSelected.setEnabled(True)
            copyAction.setEnabled(True)
            cutAction.setEnabled(True)
            codePadAction.setEnabled(True)
        if not self.text() == '':
            selectAllAction.setEnabled(True)
        if self.isUndoAvailable():
            undoAction.setEnabled(True)
        if self.isRedoAvailable():
            redoAction.setEnabled(True)
        if QApplication.clipboard().text() != "":
            pasteAction.setEnabled(True)
        action = menu.exec_(self.mapToGlobal(e.pos()))

    def objectListEditor(self):
        listObj = self.parent.pc.listClassMethod
        if listObj.isVisible():
             listObj.hide()
             self.parent.pc.objectListButton.setChecked(False)
        else:
            listObj.show()
            self.parent.pc.objectListButton.setChecked(True)
    
    def codepad(self):
        import urllib2, urllib
        listText = self.selectedText().split('\n')
        getCmd = []
        for strLine in listText:
            if strLine != "":
            #if s[0:3] in (">>>", "..."):
                # filter for special command (_save,_clear) and comment
                if strLine[4] != "_" and strLine[:2] != "##":
                    strLine.replace(">>> ", "").replace("... ", "")
                    getCmd.append(unicode(strLine))
        pasteText= u"\n".join(getCmd)
        url = 'http://codepad.org'
        values = {'lang' : 'Python',
                  'code' : pasteText,
                  'submit':'Submit'}
        try:
            response = urllib2.urlopen(url, urllib.urlencode(values))
            url = response.read()
            for href in url.split("</a>"):
                if "Link:" in href:
                    ind=href.index('Link:')
                    found = href[ind+5:]
                    for i in found.split('">'):
                        if '<a href=' in i:
                             link = i.replace('<a href="',"").strip()
            if link:
                QApplication.clipboard().setText(link)
                msgText = QCoreApplication.translate('PythonConsole', 'URL copied to clipboard.')
                self.parent.pc.callWidgetMessageBarEditor(msgText)
        except urllib2.URLError, e:
            msgText = QCoreApplication.translate('PythonConsole', 'Connection error: ')
            self.parent.pc.callWidgetMessageBarEditor(msgText + str(e.args))
Esempio n. 4
0
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")
Esempio n. 5
0
class PythonEdit(QsciScintilla, code.InteractiveInterpreter):
    def __init__(self, parent=None):
        #QsciScintilla.__init__(self, parent)
        super(PythonEdit,self).__init__(parent)
        code.InteractiveInterpreter.__init__(self, locals=None)
        
        # Enable non-ascii chars for editor
        self.setUtf8(True)
        
        self.new_input_line = True 
        
        self.setMarginWidth(0, 0)
        self.setMarginWidth(1, 0)
        self.setMarginWidth(2, 0)
        
        self.buffer = []
        
        self.insertInitText()
        self.displayPrompt(False)
        
        for line in _init_commands:
            self.runsource(line)
            
        self.history = QStringList()
        self.historyIndex = 0
        # Read history command file
        self.readHistoryFile()
          
        # Brace matching: enable for a brace immediately before or after
        # the current position
        self.setBraceMatching(QsciScintilla.SloppyBraceMatch)
        #self.moveToMatchingBrace()
        #self.selectToMatchingBrace()
        
        # Current line visible with special background color
        self.setCaretLineVisible(True)
        self.setCaretLineBackgroundColor(QColor("#ffe4e4"))
        self.setCaretWidth(2)
    
        # Set Python lexer
        # Set style for Python comments (style number 1) to a fixed-width
        # courier.
        self.setLexers()
               
        # Indentation
        #self.setAutoIndent(True)
        #self.setIndentationsUseTabs(False)
        #self.setIndentationWidth(4)
        #self.setTabIndents(True)
        #self.setBackspaceUnindents(True)
        #self.setTabWidth(4)

        self.setAutoCompletionThreshold(2)
        self.setAutoCompletionSource(self.AcsAPIs)

        # Don't want to see the horizontal scrollbar at all
        # Use raw message to Scintilla here (all messages are documented
        # here: http://www.scintilla.org/ScintillaDoc.html)
        self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0)
    
        # not too small
        #self.setMinimumSize(500, 300)
        self.setMinimumHeight(125)
        
        self.SendScintilla(QsciScintilla.SCI_SETWRAPMODE, 1)
        self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER)
        
        ## Disable command key
        ctrl, shift = self.SCMOD_CTRL<<16, self.SCMOD_SHIFT<<16
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L')+ ctrl)
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T')+ ctrl)
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D')+ ctrl)
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Z')+ ctrl)
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Y')+ ctrl)
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L')+ ctrl+shift)
        
        ## New QShortcut = ctrl+space/ctrl+alt+space for Autocomplete
        self.newShortcutCS = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Space), self)
        self.newShortcutCAS = QShortcut(QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_Space), self)
        self.newShortcutCS.activated.connect(self.autoComplete)
        self.newShortcutCAS.activated.connect(self.showHistory)
        self.connect(self, SIGNAL('userListActivated(int, const QString)'),
                     self.completion_list_selected)
    
    def showHistory(self):
        self.showUserList(1, QStringList(self.history))
    
    def autoComplete(self):
        self.autoCompleteFromAll()
       
    def clearConsole(self):
        """Clear the contents of the console."""
        self.SendScintilla(QsciScintilla.SCI_CLEARALL)
        #self.setText('')
        self.insertInitText()
        self.displayPrompt(False)
        self.setFocus()
        
    def commandConsole(self, command):
        if not self.is_cursor_on_last_line():
            self.move_cursor_to_end()
        line, pos = self.getCursorPosition()
        selCmdLenght = self.text(line).length()
        self.setSelection(line, 4, line, selCmdLenght)
        self.removeSelectedText()
        if command == "iface":
            """Import QgisInterface class"""
            self.append('from qgis.utils import iface')
            self.move_cursor_to_end()
        elif command == "sextante":
            """Import Sextante class"""
            self.append('from sextante.core.Sextante import Sextante')
            self.move_cursor_to_end()
        elif command == "cLayer":
            """Retrieve current Layer from map camvas"""
            self.append('cLayer = iface.mapCanvas().currentLayer()')
            self.move_cursor_to_end()
        elif command == "qtCore":
            """Import QtCore class"""
            self.append('from PyQt4.QtCore import *')
            self.move_cursor_to_end()
        elif command == "qtGui":
            """Import QtGui class"""
            self.append('from PyQt4.QtGui import *')
            self.move_cursor_to_end()
        self.setFocus()

    def setLexers(self):
        from qgis.core import QgsApplication
        
        self.lexer = QsciLexerPython()
        settings = QSettings()
        loadFont = settings.value("pythonConsole/fontfamilytext", "Monospace").toString()
        fontSize = settings.value("pythonConsole/fontsize", 10).toInt()[0]
        
        font = QFont(loadFont)
        font.setFixedPitch(True)
        font.setPointSize(fontSize)
        
        self.lexer.setDefaultFont(font)
        self.lexer.setColor(Qt.red, 1)
        self.lexer.setColor(Qt.darkGreen, 5)
        self.lexer.setColor(Qt.darkBlue, 15)
        self.lexer.setFont(font, 1)
        self.lexer.setFont(font, 3)
        self.lexer.setFont(font, 4)
        
        self.api = QsciAPIs(self.lexer)
        chekBoxAPI = settings.value( "pythonConsole/preloadAPI" ).toBool()
        if chekBoxAPI:
            self.api.loadPrepared( QgsApplication.pkgDataPath() + "/python/qsci_apis/pyqgis_master.pap" )
        else:
            apiPath = settings.value("pythonConsole/userAPI").toStringList()
            for i in range(0, len(apiPath)):
                self.api.load(QString(unicode(apiPath[i])))        
            self.api.prepare()
            self.lexer.setAPIs(self.api)

        self.setLexer(self.lexer)
            
    ## TODO: show completion list for file and directory
    
    def completion_list_selected(self, id, txt):
        if id == 1:
            txt = unicode(txt)
            # get current cursor position 
            line, pos = self.getCursorPosition()
            selCmdLength = self.text(line).length()
            # select typed text
            self.setSelection(line, 4, line, selCmdLength)
            self.removeSelectedText()
            self.insert(txt)

    def insertInitText(self):
        #self.setLexers(False)
        txtInit = QCoreApplication.translate("PythonConsole",
                                             "## To access Quantum GIS environment from this console\n"
                                             "## use qgis.utils.iface object (instance of QgisInterface class). Read help for more info.\n\n")
        initText = self.setText(txtInit)

    def getText(self):
        """ Get the text as a unicode string. """
        value = self.getBytes().decode('utf-8')
        # print (value) printing can give an error because the console font
        # may not have all unicode characters
        return value

    def getBytes(self):
        """ Get the text as bytes (utf-8 encoded). This is how
        the data is stored internally. """
        len = self.SendScintilla(self.SCI_GETLENGTH)+1
        bb = QByteArray(len,'0')
        N = self.SendScintilla(self.SCI_GETTEXT, len, bb)
        return bytes(bb)[:-1]

    def getTextLength(self):
        return self.SendScintilla(QsciScintilla.SCI_GETLENGTH)

    def get_end_pos(self):
        """Return (line, index) position of the last character"""
        line = self.lines() - 1
        return (line, self.text(line).length())
    
    def is_cursor_at_end(self):
        """Return True if cursor is at the end of text"""
        cline, cindex = self.getCursorPosition()
        return (cline, cindex) == self.get_end_pos()
    
    def move_cursor_to_end(self):
        """Move cursor to end of text"""
        line, index = self.get_end_pos()
        self.setCursorPosition(line, index)
        self.ensureCursorVisible()
        self.ensureLineVisible(line)
        
#    def on_new_line(self):
#        """On new input line"""
#        self.move_cursor_to_end()
#        self.new_input_line = False
        
    def is_cursor_on_last_line(self):
        """Return True if cursor is on the last line"""
        cline, _ = self.getCursorPosition()
        return cline == self.lines() - 1

    def is_cursor_on_edition_zone(self):
        """ Return True if the cursor is in the edition zone """
        cline, cindex = self.getCursorPosition()
        return cline == self.lines() - 1 and cindex >= 4
    
    def new_prompt(self, prompt):
        """
        Print a new prompt and save its (line, index) position
        """
        self.write(prompt, prompt=True)
        # now we update our cursor giving end of prompt
        line, index = self.getCursorPosition()
        self.ensureCursorVisible()
        self.ensureLineVisible(line)
        
    def refreshLexerProperties(self):
        self.setLexers()
        
#    def check_selection(self):
#        """
#        Check if selected text is r/w,
#        otherwise remove read-only parts of selection
#        """
#        #if self.current_prompt_pos is None:
#            #self.move_cursor_to_end()
#            #return
#        line_from, index_from, line_to, index_to = self.getSelection()
#        pline, pindex = self.getCursorPosition()
#        if line_from < pline or \
#           (line_from == pline and index_from < pindex):
#            self.setSelection(pline, pindex, line_to, index_to)

    def displayPrompt(self, more=False):
        self.append("... ") if more else self.append(">>> ")
        self.move_cursor_to_end()
        
    def updateHistory(self, command):
        if isinstance(command, QStringList):
            for line in command:
                self.history.append(line)
        elif not command == "":
            if len(self.history) <= 0 or \
            not command == self.history[-1]:
                self.history.append(command)
        self.historyIndex = len(self.history)
        
    def writeHistoryFile(self):
        wH = open(_historyFile, 'w')
        for s in self.history:
            wH.write(s + '\n')
        wH.close()
        
    def readHistoryFile(self):
        fileExist = QFile.exists(_historyFile)
        if fileExist:
            rH = open(_historyFile, 'r')
            for line in rH:
                if line != "\n":
                    l = line.rstrip('\n')
                    self.updateHistory(l)
        else:
            return
        
    def clearHistoryFile(self):
        cH = open(_historyFile, 'w')
        cH.close()
        
    def showPrevious(self):
        if self.historyIndex < len(self.history) and not self.history.isEmpty():
            line, pos = self.getCursorPosition()
            selCmdLenght = self.text(line).length()
            self.setSelection(line, 4, line, selCmdLenght)
            self.removeSelectedText()
            self.historyIndex += 1
            if self.historyIndex == len(self.history):
                self.insert("")
                pass
            else:
                self.insert(self.history[self.historyIndex])
            self.move_cursor_to_end()
            #self.SendScintilla(QsciScintilla.SCI_DELETEBACK)
            
    def showNext(self):
        if  self.historyIndex > 0 and not self.history.isEmpty():
            line, pos = self.getCursorPosition()
            selCmdLenght = self.text(line).length()
            self.setSelection(line, 4, line, selCmdLenght)
            self.removeSelectedText()
            self.historyIndex -= 1
            if self.historyIndex == len(self.history):
                self.insert("")
            else:
                self.insert(self.history[self.historyIndex])
            self.move_cursor_to_end()
            #self.SendScintilla(QsciScintilla.SCI_DELETEBACK)

    def keyPressEvent(self, e):  
        startLine, _, endLine, _ = self.getSelection()

        # handle invalid cursor position and multiline selections
        if not self.is_cursor_on_edition_zone() or startLine < endLine:
            # allow to copy and select
            if e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier):
                if e.key() in (Qt.Key_C, Qt.Key_A):
                    QsciScintilla.keyPressEvent(self, e)
                return
            # allow selection
            if e.modifiers() & Qt.ShiftModifier:
                if e.key() in (Qt.Key_Left, Qt.Key_Right, Qt.Key_Home, Qt.Key_End):
                    QsciScintilla.keyPressEvent(self, e)
                return

            # all other keystrokes get sent to the input line
            self.move_cursor_to_end()

        line, index = self.getCursorPosition()
        cmd = self.text(line)

        if e.key() in (Qt.Key_Return, Qt.Key_Enter) and not self.isListActive():
            self.entered()

        elif e.key() in (Qt.Key_Left, Qt.Key_Home):
            QsciScintilla.keyPressEvent(self, e)
            # check whether the cursor is moved out of the edition zone
            newline, newindex = self.getCursorPosition()
            if newline < line or newindex < 4:
                # fix selection and the cursor position
                if self.hasSelectedText():
                    self.setSelection(line, self.getSelection()[3], line, 4)
                else:
                    self.setCursorPosition(line, 4)

        elif e.key() in (Qt.Key_Backspace, Qt.Key_Delete):
            QsciScintilla.keyPressEvent(self, e)
            # check whether the cursor is moved out of the edition zone
            _, newindex = self.getCursorPosition()
            if newindex < 4:
                # restore the prompt chars (if removed) and
                # fix the cursor position
                self.insert( cmd[:3-newindex] + " " )
                self.setCursorPosition(line, 4)

        elif e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier) and \
                e.key() == Qt.Key_V:
            self.paste()
            e.accept()

        elif e.key() == Qt.Key_Down and not self.isListActive():
            self.showPrevious()
        elif e.key() == Qt.Key_Up and not self.isListActive():
            self.showNext()
        ## TODO: press event for auto-completion file directory
        else:
            QsciScintilla.keyPressEvent(self, e)
                
    def mousePressEvent(self, e):
        """
        Re-implemented to handle the mouse press event.
        e: the mouse press event (QMouseEvent)
        """
        self.setFocus()
        if e.button() == Qt.MidButton:
            stringSel = unicode(QApplication.clipboard().text(QClipboard.Selection))
            if not self.is_cursor_on_last_line():
                self.move_cursor_to_end()
            self.insertFromDropPaste(stringSel)
            e.accept()
        else:
            QsciScintilla.mousePressEvent(self, e)
                
    def paste(self):
        """
        Method to display data from the clipboard. 

        XXX: It should reimplement the virtual QScintilla.paste method, 
        but it seems not used by QScintilla code.
        """
        stringPaste = unicode(QApplication.clipboard().text())
        if self.is_cursor_on_last_line():
            if self.hasSelectedText():
                self.removeSelectedText()
        else:
            self.move_cursor_to_end()
        self.insertFromDropPaste(stringPaste)
        
    ## Drag and drop
    def dropEvent(self, e):
        if e.mimeData().hasText():
            stringDrag = e.mimeData().text()
            self.insertFromDropPaste(stringDrag)
            self.setFocus()
            e.setDropAction(Qt.MoveAction)
            e.accept()
        else:
            QsciScintillaCompat.dropEvent(self, e)

    def insertFromDropPaste(self, textDP): 
        pasteList = textDP.split("\n")
        for line in pasteList[:-1]:
            self.insert(line)
            self.move_cursor_to_end()
            #self.SendScintilla(QsciScintilla.SCI_DELETEBACK)
            self.runCommand(unicode(self.currentCommand()))
        if pasteList[-1] != "":
            self.insert(unicode(pasteList[-1]))
            self.move_cursor_to_end()

    def getTextFromEditor(self):
        text = self.text()
        textList = text.split("\n")
        return textList
    
    def insertTextFromFile(self, listOpenFile):
        for line in listOpenFile[:-1]:
            self.append(line)
            self.move_cursor_to_end()
            self.SendScintilla(QsciScintilla.SCI_DELETEBACK)
            self.runCommand(unicode(self.currentCommand()))
        self.append(unicode(listOpenFile[-1]))
        self.move_cursor_to_end()
        self.SendScintilla(QsciScintilla.SCI_DELETEBACK)
            
    def entered(self):
        self.move_cursor_to_end()
        self.runCommand( unicode(self.currentCommand()) )
        self.setFocus()
        self.move_cursor_to_end()
        #self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER)
        
    def currentCommand(self):
        linenr, index = self.getCursorPosition()
        #for i in range(0, linenr):
        txtLength = self.text(linenr).length()
        string = self.text()
        cmdLine = string.right(txtLength - 4)
        cmd = unicode(cmdLine)
        return cmd

    def runCommand(self, cmd):
        import webbrowser
        self.updateHistory(cmd)
        self.SendScintilla(QsciScintilla.SCI_NEWLINE)
        if cmd in ('_save', '_clear', '_clearAll', '_pyqgis', '_api'):
            if cmd == '_save':
                self.writeHistoryFile()
                print QCoreApplication.translate("PythonConsole", 
                                                 "## History saved successfully ##")
            elif cmd == '_clear':
                self.clearHistoryFile()
                print QCoreApplication.translate("PythonConsole", 
                                                 "## History cleared successfully ##")
            elif cmd == '_clearAll':
                res = QMessageBox.question(self, "Python Console", 
                                           QCoreApplication.translate("PythonConsole", 
                                                                      "Are you sure you want to completely\n"
                                                                      "delete the command history ?"),
                                                                      QMessageBox.Yes | QMessageBox.No)
                if res == QMessageBox.No:
                    self.SendScintilla(QsciScintilla.SCI_DELETEBACK)
                    return
                self.history = QStringList()
                self.clearHistoryFile()
                print QCoreApplication.translate("PythonConsole", 
                                                 "## History cleared successfully ##")
            elif cmd == '_pyqgis':
                webbrowser.open( "http://www.qgis.org/pyqgis-cookbook/" )
            elif cmd == '_api':
                webbrowser.open( "http://www.qgis.org/api/" )
                
            output = sys.stdout.get_and_clean_data()
            if output:
                self.append(output)
            self.displayPrompt(False)
        else:
            self.buffer.append(cmd)
            src = u"\n".join(self.buffer)
            more = self.runsource(src, "<input>")
            if not more:
                self.buffer = []
            output = sys.stdout.get_and_clean_data()
            if output:
                self.append(output)
            self.move_cursor_to_end()
            self.displayPrompt(more)

    def write(self, txt):
        self.SendScintilla(QsciScintilla.SCI_SETSTYLING, len(txt), 1)
        self.append(txt)
        self.SendScintilla(QsciScintilla.SCI_SETSTYLING, len(txt), 1)
Esempio n. 6
0
class CommandShell(QsciScintilla):
    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 text_changed(self):
        if not self.get_data().strip():
            return

        try:
            completions = command.completions_for_line(self.get_data())
            if completions == self._lastcompletions:
                self.autoCompleteFromAPIs()
                return

            self._lastcompletions = completions

            self.apis.cancelPreparation()
            self.apis.clear()
            for value in completions:
                data = u"{}".format(value)
                self.apis.add(data)

            self.apis.prepare()
        except command.NoFunction:
            return

    def end(self):
        self.parent().removeEventFilter(self)
        self.close()

    def eventFilter(self, object, event):
        if event.type() == QEvent.Resize:
            self.adjust_size()
        return QWidget.eventFilter(self, object, event)

    def keyPressEvent(self, e):
        if e.key() in (Qt.Key_Return, Qt.Key_Enter):
            self.entered()
        elif e.key() == Qt.Key_Escape:
            self.close()
        elif e.key() in (Qt.Key_Backspace, Qt.Key_Delete):
            _, newindex = self.getCursorPosition()
            if newindex > len(self.prompt):
                QsciScintilla.keyPressEvent(self, e)
        else:
            QsciScintilla.keyPressEvent(self, e)

    def show_prompt(self, prompt=_start_prompt, data=None):
        self.clear()
        if not prompt == _start_prompt:
            prompt += ":"

        text = prompt

        if data:
            text = prompt + str(data)

        self.setText(text)
        self.prompt = prompt
        self.move_cursor_to_end()

    def get_end_pos(self):
        """Return (line, index) position of the last character"""
        line = self.lines() - 1
        return (line, len(self.text(line)))

    def move_cursor_to_end(self):
        """Move cursor to end of text"""
        line, index = self.get_end_pos()
        self.setCursorPosition(line, index)
        self.ensureCursorVisible()
        self.ensureLineVisible(line)

    def get_data(self):
        line = self.text()
        line = line[len(self.prompt):]
        return line

    def entered(self):
        line = self.get_data()
        if not self.currentfunction:
            try:
                gen = command.parse_line_data(line)
            except command.NoFunction:
                return

            if gen:
                self.currentfunction = gen
                line = None
            else:
                self.currentfunction = None
                self.show_prompt()
                return

        try:
            prompt, data = self.currentfunction.send(line)
            self.show_prompt(prompt, data)
        except StopIteration:
            self.currentfunction = None
            self.show_prompt()

    def setLexers(self):
        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.lex.setFont(font)
        self.lex.setDefaultFont(font)
        self.setLexer(self.lex)

    def adjust_size(self):
        fm = QFontMetrics(self.font())
        self.setMaximumHeight(20)
        self.resize(self.parent().width(), 20)
        self.move(0, self.parent().height() - self.height())

    def showEvent(self, event):
        self.adjust_size()
        self.show_prompt()
        self.setFocus()

    def activated(self):
        visible = self.isVisible()
        self.setVisible(not visible)
Esempio n. 7
0
class ScintillaPythonEditBox(QsciScintilla, BasePythonEditBox):
    def __init__(self, parent=None):
        QsciScintilla.__init__(self, parent)

        self.lexer = None
        self.api = None
        self.lexerType = -1

        self.setCommonOptions()
        self.initShortcuts()

    def setCommonOptions(self):
        # Enable non-ASCII characters
        self.setUtf8(True)

        # Default font
        # Load font from Python console settings
        settings = QSettings()
        fontName = settings.value('pythonConsole/fontfamilytext', 'Monospace')
        fontSize = int(settings.value('pythonConsole/fontsize', 10))

        self.defaultFont = QFont(fontName)
        self.defaultFont.setFixedPitch(True)
        self.defaultFont.setPointSize(fontSize)
        self.defaultFont.setStyleHint(QFont.TypeWriter)
        self.defaultFont.setStretch(QFont.SemiCondensed)
        self.defaultFont.setLetterSpacing(QFont.PercentageSpacing, 87.0)
        self.defaultFont.setBold(False)

        self.boldFont = QFont(self.defaultFont)
        self.boldFont.setBold(True)

        self.italicFont = QFont(self.defaultFont)
        self.italicFont.setItalic(True)

        self.setFont(self.defaultFont)
        self.setMarginsFont(self.defaultFont)

        self.setFont(self.defaultFont)
        self.setMarginsFont(self.defaultFont)

        self.initLexer()

        self.setBraceMatching(QsciScintilla.SloppyBraceMatch)

        self.setWrapMode(QsciScintilla.WrapWord)
        self.setWrapVisualFlags(QsciScintilla.WrapFlagByText,
                                QsciScintilla.WrapFlagNone, 4)

        self.setSelectionForegroundColor(QColor('#2e3436'))
        self.setSelectionBackgroundColor(QColor('#babdb6'))

        # Show line numbers
        self.setMarginWidth(1, '000')
        self.setMarginLineNumbers(1, True)
        self.setMarginsForegroundColor(QColor('#2e3436'))
        self.setMarginsBackgroundColor(QColor('#babdb6'))

        # Highlight current line
        self.setCaretLineVisible(True)
        self.setCaretLineBackgroundColor(QColor('#d3d7cf'))

        # Mark column 80 with vertical line
        self.setEdgeMode(QsciScintilla.EdgeLine)
        self.setEdgeColumn(80)
        self.setEdgeColor(QColor('#eeeeec'))

        # Indentation
        self.setAutoIndent(True)
        self.setIndentationsUseTabs(False)
        self.setIndentationWidth(4)
        self.setTabIndents(True)
        self.setBackspaceUnindents(True)
        self.setTabWidth(4)

        # Autocomletion
        self.setAutoCompletionThreshold(2)
        self.setAutoCompletionSource(QsciScintilla.AcsAll)

    def initShortcuts(self):
        (ctrl, shift) = (self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16)

        # Disable some shortcuts
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D') + ctrl)
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl)
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY,
                           ord('L') + ctrl + shift)
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T') + ctrl)

        #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Z") + ctrl)
        #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Y") + ctrl)

        # Use Ctrl+Space for autocompletion
        self.shortcutAutocomplete = QShortcut(
            QKeySequence(Qt.CTRL + Qt.Key_Space), self)
        self.shortcutAutocomplete.setContext(Qt.WidgetShortcut)
        self.shortcutAutocomplete.activated.connect(self.autoComplete)

    def autoComplete(self):
        self.autoCompleteFromAll()

    def setLexerType(self, lexerType):
        self.lexerType = lexerType
        self.initLexer()

    def initLexer(self):
        self.lexer = QsciLexerPython()

        colorDefault = QColor('#2e3436')
        colorComment = QColor('#c00')
        colorCommentBlock = QColor('#3465a4')
        colorNumber = QColor('#4e9a06')
        colorType = QColor('#4e9a06')
        colorKeyword = QColor('#204a87')
        colorString = QColor('#ce5c00')

        self.lexer.setDefaultFont(self.defaultFont)
        self.lexer.setDefaultColor(colorDefault)

        self.lexer.setColor(colorComment, 1)
        self.lexer.setColor(colorNumber, 2)
        self.lexer.setColor(colorString, 3)
        self.lexer.setColor(colorString, 4)
        self.lexer.setColor(colorKeyword, 5)
        self.lexer.setColor(colorString, 6)
        self.lexer.setColor(colorString, 7)
        self.lexer.setColor(colorType, 8)
        self.lexer.setColor(colorCommentBlock, 12)
        self.lexer.setColor(colorString, 15)

        self.lexer.setFont(self.italicFont, 1)
        self.lexer.setFont(self.boldFont, 5)
        self.lexer.setFont(self.boldFont, 8)
        self.lexer.setFont(self.italicFont, 12)

        self.api = QsciAPIs(self.lexer)

        settings = QSettings()
        useDefaultAPI = bool(settings.value('pythonConsole/preloadAPI', True))
        if useDefaultAPI:
            # Load QGIS API shipped with Python console
            self.api.loadPrepared(
                os.path.join(QgsApplication.pkgDataPath(), 'python',
                             'qsci_apis', 'pyqgis.pap'))
        else:
            # Load user-defined API files
            apiPaths = settings.value('pythonConsole/userAPI', [])
            for path in apiPaths:
                self.api.load(path)
            self.api.prepare()
            self.lexer.setAPIs(self.api)

        self.setLexer(self.lexer)

    #impliment base editor
    def insertPlainText(self, text):
        self.insert(text)
        pos = self.getCursorPosition()
        self.setCursorPosition(pos[0], pos[1] + len(text))

    def toPlainText(self):
        return self.text()

    def get_font_size(self):
        return self.font().pointSize()

    def set_font_size(self, new_point_size):
        font = self.font()
        self.setFont(QFont(font.family(), new_point_size))

    def wheelEvent(self, event):
        if event.modifiers() == Qt.ControlModifier:
            self.emit(SIGNAL("wheelEvent(QWheelEvent)"), event)
        else:
            super(ScintillaPythonEditBox, self).wheelEvent(event)
class ScintillaPythonEditBox(QsciScintilla, BasePythonEditBox):

    def __init__(self, parent=None):
        QsciScintilla.__init__(self, parent)

        self.lexer = None
        self.api = None
        self.lexerType = -1

        self.setCommonOptions()
        self.initShortcuts()

    def setCommonOptions(self):
        # Enable non-ASCII characters
        self.setUtf8(True)

        # Default font
        # Load font from Python console settings
        settings = QSettings()
        fontName = settings.value('pythonConsole/fontfamilytext', 'Monospace')
        fontSize = int(settings.value('pythonConsole/fontsize', 10))

        self.defaultFont = QFont(fontName)
        self.defaultFont.setFixedPitch(True)
        self.defaultFont.setPointSize(fontSize)
        self.defaultFont.setStyleHint(QFont.TypeWriter)
        self.defaultFont.setStretch(QFont.SemiCondensed)
        self.defaultFont.setLetterSpacing(QFont.PercentageSpacing, 87.0)
        self.defaultFont.setBold(False)

        self.boldFont = QFont(self.defaultFont)
        self.boldFont.setBold(True)

        self.italicFont = QFont(self.defaultFont)
        self.italicFont.setItalic(True)

        self.setFont(self.defaultFont)
        self.setMarginsFont(self.defaultFont)

        self.setFont(self.defaultFont)
        self.setMarginsFont(self.defaultFont)

        self.initLexer()

        self.setBraceMatching(QsciScintilla.SloppyBraceMatch)

        self.setWrapMode(QsciScintilla.WrapWord)
        self.setWrapVisualFlags(QsciScintilla.WrapFlagByText,
                                QsciScintilla.WrapFlagNone, 4)

        self.setSelectionForegroundColor(QColor('#2e3436'))
        self.setSelectionBackgroundColor(QColor('#babdb6'))

        # Show line numbers
        self.setMarginWidth(1, '000')
        self.setMarginLineNumbers(1, True)
        self.setMarginsForegroundColor(QColor('#2e3436'))
        self.setMarginsBackgroundColor(QColor('#babdb6'))

        # Highlight current line
        self.setCaretLineVisible(True)
        self.setCaretLineBackgroundColor(QColor('#d3d7cf'))

        # Mark column 80 with vertical line
        self.setEdgeMode(QsciScintilla.EdgeLine)
        self.setEdgeColumn(80)
        self.setEdgeColor(QColor('#eeeeec'))

        # Indentation
        self.setAutoIndent(True)
        self.setIndentationsUseTabs(False)
        self.setIndentationWidth(4)
        self.setTabIndents(True)
        self.setBackspaceUnindents(True)
        self.setTabWidth(4)

        # Autocomletion
        self.setAutoCompletionThreshold(2)
        self.setAutoCompletionSource(QsciScintilla.AcsAll)


    def initShortcuts(self):
        (ctrl, shift) = (self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16)

        # Disable some shortcuts
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D') + ctrl)
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl)
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl
                           + shift)
        self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T') + ctrl)

        #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Z") + ctrl)
        #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Y") + ctrl)

        # Use Ctrl+Space for autocompletion
        self.shortcutAutocomplete = QShortcut(QKeySequence(Qt.CTRL
                + Qt.Key_Space), self)
        self.shortcutAutocomplete.setContext(Qt.WidgetShortcut)
        self.shortcutAutocomplete.activated.connect(self.autoComplete)

    def autoComplete(self):
        self.autoCompleteFromAll()

    def setLexerType(self, lexerType):
        self.lexerType = lexerType
        self.initLexer()

    def initLexer(self):
        self.lexer = QsciLexerPython()

        colorDefault = QColor('#2e3436')
        colorComment = QColor('#c00')
        colorCommentBlock = QColor('#3465a4')
        colorNumber = QColor('#4e9a06')
        colorType = QColor('#4e9a06')
        colorKeyword = QColor('#204a87')
        colorString = QColor('#ce5c00')

        self.lexer.setDefaultFont(self.defaultFont)
        self.lexer.setDefaultColor(colorDefault)

        self.lexer.setColor(colorComment, 1)
        self.lexer.setColor(colorNumber, 2)
        self.lexer.setColor(colorString, 3)
        self.lexer.setColor(colorString, 4)
        self.lexer.setColor(colorKeyword, 5)
        self.lexer.setColor(colorString, 6)
        self.lexer.setColor(colorString, 7)
        self.lexer.setColor(colorType, 8)
        self.lexer.setColor(colorCommentBlock, 12)
        self.lexer.setColor(colorString, 15)

        self.lexer.setFont(self.italicFont, 1)
        self.lexer.setFont(self.boldFont, 5)
        self.lexer.setFont(self.boldFont, 8)
        self.lexer.setFont(self.italicFont, 12)

        self.api = QsciAPIs(self.lexer)

        settings = QSettings()
        useDefaultAPI = bool(settings.value('pythonConsole/preloadAPI',
                                            True))
        if useDefaultAPI:
            # Load QGIS API shipped with Python console
            self.api.loadPrepared(
                os.path.join(QgsApplication.pkgDataPath(),
                             'python', 'qsci_apis', 'pyqgis.pap'))
        else:
            # Load user-defined API files
            apiPaths = settings.value('pythonConsole/userAPI', [])
            for path in apiPaths:
                self.api.load(path)
            self.api.prepare()
            self.lexer.setAPIs(self.api)

        self.setLexer(self.lexer)

    #impliment base editor
    def insertPlainText(self, text):
        self.insert(text)
        pos = self.getCursorPosition()
        self.setCursorPosition(pos[0], pos[1]+len(text))

    def toPlainText(self):
        return self.text()

    def get_font_size(self):
        return self.font().pointSize()

    def set_font_size(self, new_point_size):
        font = self.font()
        self.setFont(QFont(font.family(), new_point_size))

    def wheelEvent(self, event):
        if event.modifiers() == Qt.ControlModifier:
            self.emit(SIGNAL("wheelEvent(QWheelEvent)"), event)
        else:
            super(ScintillaPythonEditBox, self).wheelEvent(event)
Esempio n. 9
0
class CommandShell(QsciScintilla):
    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
        self.autocompletemodel = QStandardItemModel()
        self.autocompletefilter = QSortFilterProxyModel()
        self.autocompletefilter.setSourceModel(self.autocompletemodel)
        self.autocompleteview = QListView(self.parent())
        self.autocompleteview.setModel(self.autocompletefilter)
        self.autocompleteview.hide()
        self.selectionmodel = self.autocompleteview.selectionModel()

    def adjust_auto_complete(self):
        self.autocompleteview.resize(self.parent().width(), 100)
        self.autocompleteview.move(0, self.parent().height() - self.height() -
                                   self.autocompleteview.height())

    def add_completions(self, completions):
        self.autocompletemodel.clear()
        for value in completions:
            data = u"{}".format(unicode(value))
            self.autocompletemodel.appendRow(QStandardItem(data))
        self._lastcompletions = completions

    def text_changed(self):
        completions, userdata = command.completions_for_line(self.get_data())
        if not completions == self._lastcompletions:
            self.add_completions(completions)

        fuzzy = "".join(["{}.*".format(c) for c in userdata])
        self.autocompletefilter.setFilterRegExp(fuzzy)
        index = self.autocompletefilter.index(0, 0)
        if index.isValid():
            self.selectionmodel.select(index, QItemSelectionModel.Select)

        hasdata = self.autocompletemodel.rowCount() > 0

        self.adjust_auto_complete()
        self.autocompleteview.setVisible(hasdata)

    def end(self):
        self.parent().removeEventFilter(self)
        self.close()

    def eventFilter(self, object, event):
        if event.type() == QEvent.Resize:
            self.adjust_size()
        return QWidget.eventFilter(self, object, event)

    def keyPressEvent(self, e):
        if e.key() in (Qt.Key_Return, Qt.Key_Enter):
            self.entered()
        elif e.key() == Qt.Key_Escape:
            self.close()
        elif e.key() == Qt.Key_Tab:
            self.complete()
        elif e.key() in (Qt.Key_Backspace, Qt.Key_Delete):
            _, newindex = self.getCursorPosition()
            if newindex > len(self.prompt):
                QsciScintilla.keyPressEvent(self, e)
        else:
            QsciScintilla.keyPressEvent(self, e)

    def complete(self):
        try:
            index = self.autocompleteview.selectedIndexes()[0]
        except IndexError:
            return

        if index.isValid():
            text = self.autocompletefilter.data(index)
            # Wrap text with space in quotes
            if ' ' in text:
                text = "'{}'".format(text)

            line = self.get_data()
            space = line.rfind(' ')
            newline = line[:space + 1] + text + " "
            self.show_prompt(self.prompt, newline)

    def close(self):
        self.currentfunction = None
        self.autocompleteview.close()
        super(CommandShell, self).close()

    def show_prompt(self, prompt=_start_prompt, data=None):
        self.clear()
        if not prompt == _start_prompt:
            prompt += ":"

        text = prompt

        if data:
            text = prompt + str(data)

        self.setText(text)
        self.prompt = prompt
        self.move_cursor_to_end()

    def get_end_pos(self):
        """Return (line, index) position of the last character"""
        line = self.lines() - 1
        return (line, len(self.text(line)))

    def move_cursor_to_end(self):
        """Move cursor to end of text"""
        line, index = self.get_end_pos()
        self.setCursorPosition(line, index)
        self.ensureCursorVisible()
        self.ensureLineVisible(line)

    def get_data(self):
        line = self.text()
        line = line[len(self.prompt):]
        return line

    def entered(self):
        print self.currentfunction
        line = self.get_data()
        if not line:
            return

        if not self.currentfunction:
            try:
                gen = command.parse_line_data(line)
            except command.NoFunction:
                return

            if gen:
                self.currentfunction = gen
                line = None
            else:
                self.currentfunction = None
                self.show_prompt()
                return

        try:
            print "Sednign something to the function"
            prompt, data = self.currentfunction.send(line)
            self.show_prompt(prompt, data)
        except StopIteration:
            self.currentfunction = None
            self.show_prompt()
        except command.NoFunction:
            self.currentfunction = None

    def setLexers(self):
        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.lex.setFont(font)
        self.lex.setDefaultFont(font)
        self.setLexer(self.lex)

    def adjust_size(self):
        fm = QFontMetrics(self.font())
        self.setMaximumHeight(20)
        self.resize(self.parent().width(), 20)
        self.move(0, self.parent().height() - self.height())
        self.adjust_auto_complete()

    def showEvent(self, event):
        self.adjust_size()
        self.show_prompt()
        self.setFocus()
        self.autocompleteview.show()

    def activated(self):
        visible = self.isVisible()
        self.setVisible(not visible)