Пример #1
0
class ScriptTab(QWidget,):
    autocomplete_lst = []

    def __init__(self, parent, name, script="", filemodified=0):
        QWidget.__init__(self)
        self.parent = parent
        self.tabWidget = self.parent.ui.tabWidget
        self.gridlayout = QGridLayout(self)
        self._dirty = False

        self.initialize_editor()
        self.gridlayout.addWidget(self.editor)

        self.setAcceptDrops(True)

        self.filename = name
        self.filemodified = filemodified
        if self.is_file():
            self.title = os.path.basename(name)
            # if self.equals_saved():
            #    self.filemodified = os.path.getmtime(self.filename)
        else:
            self.filename = ""
            self.title = name

        # Show this file in the self.editor
        self.editor.setText(script)
        self.clean_txt = self.saved()

        self.update_dirty()

        self.editor.keyPressEvent = self.key_press_event

    @property
    def scriptRunner(self):
        return self.parent.controller.scriptRunner

    def wheelEvent(self, event):
        if event.modifiers() == Qt.ControlModifier:
            if event.delta() > 0:
                self.editor.zoomIn()
            else:
                self.editor.zoomOut()
        return QWidget.wheelEvent(self, event)


    def initialize_editor(self):



        self.editor = QsciScintilla()

#        self.editor.cursorPositionChanged.connect(self.e)
#        self.editor.copyAvailable.connect(self.e)
#        self.editor.indicatorClicked.connect(self.e)
#        self.editor.indicatorReleased.connect(self.e)
#        self.editor.linesChanged.connect(self.e)
#        self.editor.marginClicked.connect(self.e)
#        self.editor.modificationAttempted.connect(self.e)
#        self.editor.modificationChanged.connect(self.e)
#        self.editor.selectionChanged.connect(self.e)
#        self.editor.textChanged.connect(self.e)
#        self.editor.userListActivated.connect(self.e)

        if self.editor.__class__.__name__ == "LineTextWidget":
            return  # When using PySide without QSciScintilla

        # define the font to use
        font = QFont()
        font.setFamily("Consolas")
        font.setFixedPitch(True)
        font.setPointSize(10)
        # the font metrics here will help
        # building the margin width later
        fm = QFontMetrics(font)

        # set the default font of the self.editor
        # and take the same font for line numbers
        self.editor.setFont(font)
        self.editor.setMarginsFont(font)

        # Line numbers
        # conventionnaly, margin 0 is for line numbers
        self.editor.setMarginWidth(0, fm.width("00000") + 5)
        self.editor.setMarginLineNumbers(0, True)

        self.editor.setTabWidth(4)

        # Folding visual : we will use boxes
        self.editor.setFolding(QsciScintilla.BoxedTreeFoldStyle)

        self.editor.setAutoIndent(True)

        # Braces matching
        self.editor.setBraceMatching(QsciScintilla.SloppyBraceMatch)

        # Editing line color
        self.editor.setCaretLineVisible(True)
        self.editor.setCaretLineBackgroundColor(QColor("#CDA869"))

        # Margins colors
        # line numbers margin
        self.editor.setMarginsBackgroundColor(QColor("#333333"))
        self.editor.setMarginsForegroundColor(QColor("#CCCCCC"))

        # folding margin colors (foreground,background)
        self.editor.setFoldMarginColors(QColor("#99CC66"), QColor("#333300"))

        # Choose a lexer
        self.lexer = QsciLexerPython()
        self.lexer.setDefaultFont(font)

        # Set the length of the string before the editor tries to autocomplete
        # In practise this would be higher than 1
        # But its set lower here to make the autocompletion more obvious
        self.editor.setAutoCompletionThreshold(1)
        # Tell the editor we are using a QsciAPI for the autocompletion
        self.editor.setAutoCompletionSource(QsciScintilla.AcsAPIs)

        self.editor.setLexer(self.lexer)
        self.editor.setCallTipsStyle(QsciScintilla.CallTipsContext)

        # self.editor.setCallTipsVisible(0)
        # Create an API for us to populate with our autocomplete terms
        self.api = QsciAPIs(self.lexer)

        # Compile the api for use in the lexer
        self.api.prepare()




#    def tefocusInEvent(self, event):
#        self.parent.focusInEvent(event)
#        return QTextEdit.focusInEvent(self.textEditScript, event)

    def is_file(self):
        return os.path.isfile(self.filename)

    def is_modified(self):
        if self.is_file():
            if self.equals_saved():
                self.filemodified = os.path.getmtime(self.filename)
            return str(os.path.getmtime(self.filename)) != str(self.filemodified)
        else:
            return False

    def saved(self):
        if self.is_file():
            f = open(self.filename)
            saved = f.read()
            f.close()
            return saved.strip()
        else:
            return ""

    def equals_saved(self):
        curr_lines = self.get_script().strip().splitlines()
        saved_lines = self.saved().strip().splitlines()
        if len(curr_lines) != len(saved_lines):
            return False
        for cl, sl in zip(curr_lines, saved_lines):
            if cl.strip() != sl.strip():
                return False
        return True


    @property
    def dirty(self):
        if not self._dirty:
            self.dirty = self.clean_txt != self.get_script()
        return self._dirty

    @dirty.setter
    def dirty(self, dirty):
        if dirty is False:
            self.clean_txt = self.get_script()
        if self._dirty != dirty:
            self._dirty = dirty
            self.filename_changed()

    def update_dirty(self):
        return self.dirty

    def index(self):
        return self.tabWidget.indexOf(self)

    def filename_changed(self):
        index = self.parent.ui.tabWidget.indexOf(self)
        if self.dirty:
            self.tabWidget.setTabText(index, "%s*" % self.title)
        else:
            self.tabWidget.setTabText(index, self.title)
 
    def reload(self):
        self.set_script(self.parent._load_script(self.filename))
        self.filemodified = os.path.getmtime(self.filename)
 
    def close(self):
        while self.dirty is True:  # While avoids data loss, in case save operation is aborted
            if self.filename == "":
                text = "Save unsaved changes?"
            else:
                text = "Save %s?" % self.filename
 
            ans = QMessageBox.question(self, 'Save', text, QMessageBox.Yes, QMessageBox.Cancel, QMessageBox.No)
 
            if ans == QMessageBox.Cancel:
                return
            elif ans == QMessageBox.Yes:
                self.parent.actionSave(False)
            elif ans == QMessageBox.No:
                break
        self.tabWidget.removeTab(self.index())
 
    def _save(self,):
        f = open(self.filename, 'w')
        f.write(self.get_script())
        f.close()
        self.title = os.path.basename(self.filename)
        self.dirty = False



    def key_press_event(self, event):

        self.update_dirty()
        if self.editor.__class__.__name__ == "LineTextWidget":
            return self.editor.edit.keyReleaseEvent(event)  # When using PySide without QSciScintilla

        QsciScintilla.keyPressEvent(self.editor, event)

        linenr, pos_in_line = self.editor.getCursorPosition()
        line = str(self.editor.text(linenr)[:pos_in_line])

        if event.key() == Qt.Key_F1:
            try:
                self.parent.show_documentation(getattr(self.scriptRunner, str(self.editor.selectedText())))
            except AttributeError:
                pass

        if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return:
            for tip in self.autocomplete_lst:
                try:
                    if line.endswith(tip[:tip.index('(') ]):
                        self.editor.insert(tip[tip.index('('):])
                        parent, residual = self.scriptRunner.split_line(line)
                        self.parent.show_documentation(self.scriptRunner.get_function_dict(parent, residual)[residual])
                        return
                except ValueError:
                    pass

        if event.key() < 100 or event.key() in [Qt.Key_Backspace, Qt.Key_Shift]:
            linenr, pos_in_line = self.editor.getCursorPosition()
            line = str(self.editor.text(linenr)[:pos_in_line])

            lst = self.scriptRunner.get_autocomplete_list(line)

            if lst is not None:
                self.api.clear()
                list(map(self.api.add, lst))
                self.api.prepare()
                if len(lst) > 0:
                    self.autocomplete_lst = lst

            self.editor.autoCompleteFromAll()
            shift_event = QKeyEvent(QEvent.KeyPress, Qt.Key_Shift, Qt.NoModifier)
            QsciScintilla.keyPressEvent(self.editor, shift_event)  # show autocomplete list




    def set_script(self, script):
        self.editor.setText(script)

    def get_script(self):
        return str(self.editor.text()).replace("\r", "").replace(">>> ", "").replace("\t", "    ")

    def dropHandler(self, dataItemList, ctrl, shift, event):
        pass
Пример #2
0
class APIs(QObject):
    """
    Class implementing an API storage entity.
    
    @signal apiPreparationFinished() emitted after the API preparation has
        finished
    @signal apiPreparationCancelled() emitted after the API preparation has
        been cancelled
    @signal apiPreparationStarted() emitted after the API preparation has
        started
    """
    apiPreparationFinished = pyqtSignal()
    apiPreparationCancelled = pyqtSignal()
    apiPreparationStarted = pyqtSignal()

    def __init__(self,
                 language,
                 projectType="",
                 forPreparation=False,
                 parent=None):
        """
        Constructor
        
        @param language language of the APIs object
        @type str
        @param projectType type of the project
        @type str
        @param forPreparation flag indicating this object is just needed
            for a preparation process
        @type bool
        @param parent reference to the parent object
        @type QObject
        """
        super(APIs, self).__init__(parent)
        if projectType:
            self.setObjectName("APIs_{0}_{1}".format(language, projectType))
        else:
            self.setObjectName("APIs_{0}".format(language))

        self.__inPreparation = False
        self.__language = language
        self.__projectType = projectType
        self.__forPreparation = forPreparation
        self.__lexer = Lexers.getLexer(self.__language)
        self.__apifiles = Preferences.getEditorAPI(self.__language,
                                                   self.__projectType)
        self.__apifiles.sort()
        if self.__lexer is None:
            self.__apis = None
        else:
            self.__apis = QsciAPIs(self.__lexer)
            self.__apis.apiPreparationFinished.connect(
                self.__apiPreparationFinished)
            self.__apis.apiPreparationCancelled.connect(
                self.__apiPreparationCancelled)
            self.__apis.apiPreparationStarted.connect(
                self.__apiPreparationStarted)
            self.__loadAPIs()

    def __loadAPIs(self):
        """
        Private method to load the APIs.
        """
        if self.__apis.isPrepared():
            # load a prepared API file
            if (not self.__forPreparation
                    and Preferences.getEditor("AutoPrepareAPIs")):
                self.prepareAPIs()
            self.__apis.loadPrepared(self.__preparedName())
        else:
            # load the raw files and prepare the API file
            if (not self.__forPreparation
                    and Preferences.getEditor("AutoPrepareAPIs")):
                self.prepareAPIs(ondemand=True)

    def reloadAPIs(self):
        """
        Public method to reload the API information.
        """
        if (not self.__forPreparation
                and Preferences.getEditor("AutoPrepareAPIs")):
            self.prepareAPIs()
        self.__loadAPIs()

    def getQsciAPIs(self):
        """
        Public method to get a reference to QsciAPIs object.
        
        @return reference to the QsciAPIs object (QsciAPIs)
        """
        if (not self.__forPreparation
                and Preferences.getEditor("AutoPrepareAPIs")):
            self.prepareAPIs()
        return self.__apis

    def isEmpty(self):
        """
        Public method to check, if the object has API files configured.
        
        @return flag indicating no API files have been configured (boolean)
        """
        return len(self.__apifiles) == 0

    def __apiPreparationFinished(self):
        """
        Private method called to save an API, after it has been prepared.
        """
        self.__apis.savePrepared(self.__preparedName())
        self.__inPreparation = False
        self.apiPreparationFinished.emit()

    def __apiPreparationCancelled(self):
        """
        Private method called, after the API preparation process has been
        cancelled.
        """
        self.__inPreparation = False
        self.apiPreparationCancelled.emit()

    def __apiPreparationStarted(self):
        """
        Private method called, when the API preparation process started.
        """
        self.__inPreparation = True
        self.apiPreparationStarted.emit()

    def prepareAPIs(self, ondemand=False, rawList=None):
        """
        Public method to prepare the APIs if necessary.
        
        @keyparam ondemand flag indicating a requested preparation (boolean)
        @keyparam rawList list of raw API files (list of strings)
        """
        if self.__apis is None or self.__inPreparation:
            return

        needsPreparation = False
        if ondemand:
            needsPreparation = True
        else:
            # check, if a new preparation is necessary
            preparedAPIs = self.__preparedName()
            if preparedAPIs:
                preparedAPIsInfo = QFileInfo(preparedAPIs)
                if not preparedAPIsInfo.exists():
                    needsPreparation = True
                else:
                    preparedAPIsTime = preparedAPIsInfo.lastModified()
                    apifiles = sorted(
                        Preferences.getEditorAPI(self.__language,
                                                 self.__projectType))
                    if self.__apifiles != apifiles:
                        needsPreparation = True
                    for apifile in apifiles:
                        if (QFileInfo(apifile).lastModified() >
                                preparedAPIsTime):
                            needsPreparation = True
                            break

        if needsPreparation:
            # do the preparation
            self.__apis.clear()
            if rawList:
                apifiles = rawList
            else:
                apifiles = Preferences.getEditorAPI(self.__language,
                                                    self.__projectType)
            for apifile in apifiles:
                self.__apis.load(apifile)
            self.__apis.prepare()
            self.__apifiles = apifiles

    def cancelPreparation(self):
        """
        Public slot to cancel the APIs preparation.
        """
        self.__apis and self.__apis.cancelPreparation()

    def installedAPIFiles(self):
        """
        Public method to get a list of installed API files.
        
        @return list of installed API files (list of strings)
        """
        if self.__apis is not None:
            if Globals.isWindowsPlatform():
                qsciPath = os.path.join(Globals.getPyQt5ModulesDirectory(),
                                        "qsci")
                if os.path.exists(qsciPath):
                    # it's the installer
                    if self.__lexer.lexerName() is not None:
                        apidir = os.path.join(qsciPath, "api",
                                              self.__lexer.lexerName())
                        fnames = []
                        filist = QDir(apidir).entryInfoList(["*.api"],
                                                            QDir.Files,
                                                            QDir.IgnoreCase)
                        for fi in filist:
                            fnames.append(fi.absoluteFilePath())
                        return fnames
                    else:
                        return []

            return self.__apis.installedAPIFiles()
        else:
            return []

    def __preparedName(self):
        """
        Private method returning the default name of a prepared API file.
        
        @return complete filename for the Prepared APIs file (string)
        """
        apisDir = os.path.join(Globals.getConfigDir(), "APIs")
        if self.__apis is not None:
            if self.__projectType:
                filename = "{0}_{1}.pap".format(self.__language,
                                                self.__projectType)
            else:
                filename = "{0}.pap".format(self.__language)
            return os.path.join(apisDir, filename)
        else:
            return ""
Пример #3
0
class APIs(QObject):
    """
    Class implementing an API storage entity.
    
    @signal apiPreparationFinished() emitted after the API preparation has
        finished
    @signal apiPreparationCancelled() emitted after the API preparation has
        been cancelled
    @signal apiPreparationStarted() emitted after the API preparation has
        started
    """
    apiPreparationFinished = pyqtSignal()
    apiPreparationCancelled = pyqtSignal()
    apiPreparationStarted = pyqtSignal()
    
    def __init__(self, language, forPreparation=False, parent=None):
        """
        Constructor
        
        @param language language of the APIs object (string)
        @param forPreparation flag indicating this object is just needed
            for a preparation process (boolean)
        @param parent reference to the parent object (QObject)
        """
        super(APIs, self).__init__(parent)
        self.setObjectName("APIs_{0}".format(language))
        
        self.__inPreparation = False
        self.__language = language
        self.__forPreparation = forPreparation
        self.__lexer = Lexers.getLexer(self.__language)
        self.__apifiles = Preferences.getEditorAPI(self.__language)
        self.__apifiles.sort()
        if self.__lexer is None:
            self.__apis = None
        else:
            self.__apis = QsciAPIs(self.__lexer)
            self.__apis.apiPreparationFinished.connect(
                self.__apiPreparationFinished)
            self.__apis.apiPreparationCancelled.connect(
                self.__apiPreparationCancelled)
            self.__apis.apiPreparationStarted.connect(
                self.__apiPreparationStarted)
            self.__loadAPIs()
        
    def __loadAPIs(self):
        """
        Private method to load the APIs.
        """
        if self.__apis.isPrepared():
            # load a prepared API file
            if not self.__forPreparation and \
                    Preferences.getEditor("AutoPrepareAPIs"):
                self.prepareAPIs()
            self.__apis.loadPrepared()
        else:
            # load the raw files and prepare the API file
            if not self.__forPreparation and \
                    Preferences.getEditor("AutoPrepareAPIs"):
                self.prepareAPIs(ondemand=True)
    
    def reloadAPIs(self):
        """
        Public method to reload the API information.
        """
        if not self.__forPreparation and \
                Preferences.getEditor("AutoPrepareAPIs"):
            self.prepareAPIs()
        self.__loadAPIs()
    
    def getQsciAPIs(self):
        """
        Public method to get a reference to QsciAPIs object.
        
        @return reference to the QsciAPIs object (QsciAPIs)
        """
        if not self.__forPreparation and \
                Preferences.getEditor("AutoPrepareAPIs"):
            self.prepareAPIs()
        return self.__apis
    
    def isEmpty(self):
        """
        Public method to check, if the object has API files configured.
        
        @return flag indicating no API files have been configured (boolean)
        """
        return len(self.__apifiles) == 0
    
    def __apiPreparationFinished(self):
        """
        Private method called to save an API, after it has been prepared.
        """
        self.__apis.savePrepared()
        self.__inPreparation = False
        self.apiPreparationFinished.emit()
    
    def __apiPreparationCancelled(self):
        """
        Private method called, after the API preparation process has been
        cancelled.
        """
        self.__inPreparation = False
        self.apiPreparationCancelled.emit()
    
    def __apiPreparationStarted(self):
        """
        Private method called, when the API preparation process started.
        """
        self.__inPreparation = True
        self.apiPreparationStarted.emit()
    
    def prepareAPIs(self, ondemand=False, rawList=None):
        """
        Public method to prepare the APIs if necessary.
        
        @keyparam ondemand flag indicating a requested preparation (boolean)
        @keyparam rawList list of raw API files (list of strings)
        """
        if self.__apis is None or self.__inPreparation:
            return
        
        needsPreparation = False
        if ondemand:
            needsPreparation = True
        else:
            # check, if a new preparation is necessary
            preparedAPIs = self.__defaultPreparedName()
            if preparedAPIs:
                preparedAPIsInfo = QFileInfo(preparedAPIs)
                if not preparedAPIsInfo.exists():
                    needsPreparation = True
                else:
                    preparedAPIsTime = preparedAPIsInfo.lastModified()
                    apifiles = sorted(
                        Preferences.getEditorAPI(self.__language))
                    if self.__apifiles != apifiles:
                        needsPreparation = True
                    for apifile in apifiles:
                        if QFileInfo(apifile).lastModified() > \
                                preparedAPIsTime:
                            needsPreparation = True
                            break
        
        if needsPreparation:
            # do the preparation
            self.__apis.clear()
            if rawList:
                apifiles = rawList
            else:
                apifiles = Preferences.getEditorAPI(self.__language)
            for apifile in apifiles:
                self.__apis.load(apifile)
            self.__apis.prepare()
            self.__apifiles = apifiles
    
    def cancelPreparation(self):
        """
        Public slot to cancel the APIs preparation.
        """
        self.__apis and self.__apis.cancelPreparation()
    
    def installedAPIFiles(self):
        """
        Public method to get a list of installed API files.
        
        @return list of installed API files (list of strings)
        """
        if self.__apis is not None:
            if Globals.isWindowsPlatform():
                qsciPath = os.path.join(
                    Globals.getPyQt5ModulesDirectory(), "qsci")
                if os.path.exists(qsciPath):
                    # it's the installer
                    if self.__lexer.lexerName() is not None:
                        apidir = os.path.join(qsciPath, "api",
                                              self.__lexer.lexerName())
                        fnames = []
                        filist = QDir(apidir).entryInfoList(
                            ["*.api"], QDir.Files, QDir.IgnoreCase)
                        for fi in filist:
                            fnames.append(fi.absoluteFilePath())
                        return fnames
                    else:
                        return []
            
            return self.__apis.installedAPIFiles()
        else:
            return []
    
    def __defaultPreparedName(self):
        """
        Private method returning the default name of a prepared API file.
        
        @return complete filename for the Prepared APIs file (string)
        """
        if self.__apis is not None:
            return self.__apis.defaultPreparedName()
        else:
            return ""
Пример #4
0
class CodeEditor(QsciScintilla):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.filename = None
        self.fileBrowser = None
        self.mainWindow = parent
        self.debugging = False

        c = Configuration()
        self.pointSize = int(c.getFont())
        self.tabWidth = int(c.getTab())

        # Scrollbars
        self.verticalScrollBar().setStyleSheet("""border: 20px solid black;
            background-color: darkgreen;
            alternate-background-color: #FFFFFF;""")

        self.horizontalScrollBar().setStyleSheet("""border: 20px solid black;
            background-color: darkgreen;
            alternate-background-color: #FFFFFF;""")

        # matched / unmatched brace color ...
        self.setMatchedBraceBackgroundColor(QColor('#000000'))
        self.setMatchedBraceForegroundColor(QColor('cyan'))
        self.setUnmatchedBraceBackgroundColor(QColor('#000000'))
        self.setUnmatchedBraceForegroundColor(QColor('red'))

        self.setBraceMatching(QsciScintilla.SloppyBraceMatch)

        # edge mode ... line at 79 characters
        self.setEdgeColumn(79)
        self.setEdgeMode(1)
        self.setEdgeColor(QColor('dark green'))

        # Set the default font
        self.font = QFont()
        self.font.setFamily(c.getFontFamily())
        self.font.setFixedPitch(True)
        self.font.setPointSize(self.pointSize)
        self.setFont(self.font)
        self.setMarginsFont(self.font)

        # Margin 0 is used for line numbers
        fontmetrics = QFontMetrics(self.font)
        self.setMarginsFont(self.font)
        self.setMarginWidth(0, fontmetrics.width("00000") + 5)
        self.setMarginLineNumbers(0, True)
        self.setMarginsBackgroundColor(QColor("#000000"))
        self.setMarginsForegroundColor(QColor("#FFFFFF"))

        # Margin 1 for breakpoints
        self.setMarginType(1, QsciScintilla.SymbolMargin)
        self.setMarginWidth(1, "00")
        sym = QsciScintilla.Circle

        self.markerDefine(sym, 1)

        self.setMarginMarkerMask(0, 0b1111)

        handle_01 = self.markerAdd(1, 0)

        # FoldingBox
        self.setFoldMarginColors(QColor('dark green'), QColor('dark green'))

        # CallTipBox
        self.setCallTipsForegroundColor(QColor('#FFFFFF'))
        self.setCallTipsBackgroundColor(QColor('#282828'))
        self.setCallTipsHighlightColor(QColor('#3b5784'))
        self.setCallTipsStyle(QsciScintilla.CallTipsContext)
        self.setCallTipsPosition(QsciScintilla.CallTipsBelowText)
        self.setCallTipsVisible(-1)

        # change caret's color
        self.SendScintilla(QsciScintilla.SCI_SETCARETFORE, QColor('#98fb98'))
        self.setCaretWidth(4)

        # tab Width
        self.setIndentationsUseTabs(False)
        self.setTabWidth(self.tabWidth)
        # use Whitespaces instead tabs
        self.SendScintilla(QsciScintilla.SCI_SETUSETABS, False)
        self.setAutoIndent(True)
        self.setTabIndents(True)

        # BackTab
        self.setBackspaceUnindents(True)

        # Current line visible with special background color or not :)
        #self.setCaretLineVisible(False)
        #self.setCaretLineVisible(True)
        #self.setCaretLineBackgroundColor(QColor("#020202"))

        # not too small
        self.setMinimumSize(300, 300)

        # get style
        self.style = None

        # Call the Color-Function: ...
        self.setPythonStyle()

        #self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0)

        # Contextmenu
        self.setContextMenuPolicy(Qt.ActionsContextMenu)
        undoAction = QAction("Undo", self)
        undoAction.triggered.connect(self.undoContext)
        redoAction = QAction("Redo", self)
        redoAction.triggered.connect(self.redoContext)
        sepAction1 = QAction("", self)
        sepAction1.setSeparator(True)
        cutAction = QAction("Cut", self)
        cutAction.triggered.connect(self.cutContext)
        copyAction = QAction("Copy", self)
        copyAction.triggered.connect(self.copyContext)
        pasteAction = QAction("Paste", self)
        pasteAction.triggered.connect(self.pasteContext)
        sepAction2 = QAction("", self)
        sepAction2.setSeparator(True)
        sepAction3 = QAction("", self)
        sepAction3.setSeparator(True)
        selectAllAction = QAction("Select All", self)
        selectAllAction.triggered.connect(self.getContext)
        terminalAction = QAction("Open Terminal", self)
        terminalAction.triggered.connect(self.termContext)

        self.addAction(undoAction)
        self.addAction(redoAction)
        self.addAction(sepAction1)
        self.addAction(cutAction)
        self.addAction(copyAction)
        self.addAction(pasteAction)
        self.addAction(sepAction2)
        self.addAction(selectAllAction)
        self.addAction(sepAction3)
        self.addAction(terminalAction)

        # signals
        self.SCN_FOCUSIN.connect(self.onFocusIn)
        self.textChanged.connect(self.onTextChanged)
        self.marginClicked.connect(self.onMarginClicked)

    def onFocusIn(self):
        self.mainWindow.refresh(self)

    def onTextChanged(self):
        notebook = self.mainWindow.notebook
        textPad = notebook.currentWidget()
        index = notebook.currentIndex()

        if self.debugging is True:
            self.mainWindow.statusBar.showMessage(
                'remember to update CodeView if you delete or change lines in CodeEditor !',
                3000)

        if textPad == None:
            return

        if textPad.filename:
            if not '*' in notebook.tabText(index):
                fname = os.path.basename(textPad.filename)
                fname += '*'
                notebook.setTabText(index, fname)

        else:
            fname = notebook.tabText(index)
            fname += '*'

            if not '*' in notebook.tabText(index):
                notebook.setTabText(index, fname)

    def onMarginClicked(self):
        pass

    def checkPath(self, path):
        if '\\' in path:
            path = path.replace('\\', '/')
        return path

    def undoContext(self):
        self.undo()

    def redoContext(self):
        self.redo()

    def cutContext(self):
        self.cut()

    def copyContext(self):
        self.copy()

    def pasteContext(self):
        self.paste()

    def getContext(self):
        self.selectAll()

    def termContext(self):
        c = Configuration()
        system = c.getSystem()
        command = c.getTerminal(system)

        thread = RunThread(command)
        thread.start()

    def getLexer(self):
        return self.lexer

    def setPythonStyle(self):
        self.style = 'Python'

        # Set Python lexer
        self.setAutoIndent(True)

        #self.lexer = QsciLexerPython()
        self.lexer = PythonLexer()
        self.lexer.setFont(self.font)
        self.lexer.setFoldComments(True)

        # set Lexer
        self.setLexer(self.lexer)

        self.setCaretLineBackgroundColor(QColor("#344c4c"))
        self.lexer.setDefaultPaper(QColor("black"))
        self.lexer.setDefaultColor(QColor("white"))
        self.lexer.setColor(QColor('white'), 0)  # default
        self.lexer.setPaper(QColor('black'), -1)  # default -1 vor all styles
        self.lexer.setColor(QColor('gray'), PythonLexer.Comment)  # = 1
        self.lexer.setColor(QColor('orange'), 2)  # Number = 2
        self.lexer.setColor(QColor('lightblue'), 3)  # DoubleQuotedString
        self.lexer.setColor(QColor('lightblue'), 4)  # SingleQuotedString
        self.lexer.setColor(QColor('#33cccc'), 5)  # Keyword
        self.lexer.setColor(QColor('lightblue'), 6)  # TripleSingleQuotedString
        self.lexer.setColor(QColor('lightblue'), 7)  # TripleDoubleQuotedString
        self.lexer.setColor(QColor('#ffff00'), 8)  # ClassName
        self.lexer.setColor(QColor('#ffff66'), 9)  # FunctionMethodName
        self.lexer.setColor(QColor('magenta'), 10)  # Operator
        self.lexer.setColor(QColor('white'), 11)  # Identifier
        self.lexer.setColor(QColor('gray'), 12)  # CommentBlock
        self.lexer.setColor(QColor('#ff471a'), 13)  # UnclosedString
        self.lexer.setColor(QColor('gray'), 14)  # HighlightedIdentifier
        self.lexer.setColor(QColor('#5DD3AF'), 15)  # Decorator
        self.setPythonAutocomplete()
        self.setFold()

    def setPythonAutocomplete(self):

        self.autocomplete = QsciAPIs(self.lexer)
        self.keywords = self.lexer.keywords(1)

        self.keywords = self.keywords.split(' ')

        for word in self.keywords:
            self.autocomplete.add(word)

        self.autocomplete.add('super')
        self.autocomplete.add('self')
        self.autocomplete.add('__name__')
        self.autocomplete.add('__main__')
        self.autocomplete.add('__init__')
        self.autocomplete.add('__str__')
        self.autocomplete.add('__repr__')

        self.autocomplete.prepare()

        ## Set the length of the string before the editor tries to autocomplete
        self.setAutoCompletionThreshold(3)

        ## Tell the editor we are using a QsciAPI for the autocompletion
        self.setAutoCompletionSource(QsciScintilla.AcsAPIs)

        self.updateAutoComplete()

    def setFold(self):
        # setup Fold Styles for classes and functions ...
        x = self.FoldStyle(self.FoldStyle(5))
        #self.textPad.folding()
        if not x:
            self.foldAll(False)

        self.setFolding(x)
        #self.textPad.folding()

    def unsetFold(self):
        self.setFolding(0)

    def keyReleaseEvent(self, e):
        # feed the autocomplete with the words from editor
        # simple algorithm to do this ... everytime after Enter

        # refresh CodeView
        text = self.text()
        self.updateCodeView(text)

        # if ENTER was hit ... :

        if e.key() == Qt.Key_Return:

            self.updateAutoComplete()

    def updateCodeView(self, text=''):
        codeView = self.mainWindow.codeView
        codeViewDict = codeView.makeDictForCodeView(text)
        codeView.updateCodeView(codeViewDict)

    def updateAutoComplete(self, text=None):

        if not text:

            firstList = []  # list to edit
            secondList = []  # collect all items for autocomplete

            text = self.text()

            # parse complete text ....
            firstList = text.splitlines()

            for line in firstList:
                if 'def' in line:
                    item = line.strip()
                    item = item.strip('def')
                    item = item.replace(':', '')
                    if not item in secondList:
                        secondList.append(item)
                elif 'class' in line:
                    item = line.strip()
                    item = item.strip('class')
                    item = item.replace(':', '')
                    if not item in secondList:
                        secondList.append(item)


            text = text.replace('"', " ").replace("'", " ").replace("(", " ").replace\
                                (")", " ").replace("[", " ").replace("]", " ").replace\
                                (':', " ").replace(',', " ").replace("<", " ").replace\
                                (">", " ").replace("/", " ").replace("=", " ").replace\
                                (";", " ")

            firstList = text.split('\n')

            for row in firstList:

                if (row.strip().startswith('#')) or (
                        row.strip().startswith('//')):
                    continue

                else:
                    wordList = row.split()

                    for word in wordList:

                        if re.match("(^[0-9])", word):
                            continue

                        elif '#' in word or '//' in word:
                            continue

                        elif word in self.keywords:
                            continue

                        elif (word == '__init__') or (word == '__main__') or \
                             (word == '__name__') or (word == '__str__') or \
                             (word == '__repr__'):
                            continue

                        elif word in secondList:
                            continue

                        elif len(word) > 15:
                            continue

                        elif not len(word) < 3:
                            w = re.sub("{}<>;,:]", '', word)
                            #print(w)
                            secondList.append(w)

            # delete doubled entries
            x = set(secondList)
            secondList = list(x)

            # debugging ...
            #print(secondList)

            for item in secondList:
                self.autocomplete.add(item)

            self.autocomplete.prepare()

    def setPythonPrintStyle(self):
        # Set None lexer
        self.font = QFont()
        self.font.setFamily(c.getFontFamily())
        self.font.setFixedPitch(True)
        self.font.setPointSize(10)
        self.setFont(self.font)

        self.lexer = PythonLexer()
        self.lexer.setFont(self.font)
        # set Lexer
        self.setLexer(self.lexer)

        self.setCaretLineBackgroundColor(QColor("#344c4c"))
        self.lexer.setDefaultPaper(QColor("white"))
        self.lexer.setDefaultColor(QColor("black"))
        self.lexer.setColor(QColor('black'), -1)  # default
        self.lexer.setPaper(QColor('white'), -1)  # default
        self.lexer.setColor(QColor('gray'),
                            PythonLexer.Comment)  # entspricht 1
        self.lexer.setColor(QColor('orange'), 2)  # Number entspricht 2
        self.lexer.setColor(QColor('darkgreen'),
                            3)  # DoubleQuotedString entspricht 3
        self.lexer.setColor(QColor('darkgreen'),
                            4)  # SingleQuotedString entspricht 4
        self.lexer.setColor(QColor('darkblue'), 5)  # Keyword entspricht 5
        self.lexer.setColor(QColor('darkgreen'),
                            6)  # TripleSingleQuotedString entspricht 6
        self.lexer.setColor(QColor('darkgreen'),
                            7)  # TripleDoubleQuotedString entspricht 7
        self.lexer.setColor(QColor('red'), 8)  # ClassName entspricht 8
        self.lexer.setColor(QColor('crimson'),
                            9)  # FunctionMethodName entspricht 9
        self.lexer.setColor(QColor('green'), 10)  # Operator entspricht 10
        self.lexer.setColor(QColor('black'),
                            11)  # Identifier entspricht 11 ### alle Wörter
        self.lexer.setColor(QColor('gray'), 12)  # CommentBlock entspricht 12
        self.lexer.setColor(QColor('#ff471a'),
                            13)  # UnclosedString entspricht 13
        self.lexer.setColor(QColor('gray'),
                            14)  # HighlightedIdentifier entspricht 14
        self.lexer.setColor(QColor('#5DD3AF'), 15)  # Decorator entspricht 15

        self.setNoneAutocomplete()
        self.unsetFold()

        self.font = QFont()
        self.font.setFamily('Mono')
        self.font.setFixedPitch(True)
        self.font.setPointSize(self.pointSize)

    def setNoneAutocomplete(self):
        #AutoCompletion
        self.autocomplete = Qsci.QsciAPIs(self.lexer)
        self.autocomplete.clear()

        self.autocomplete.prepare()

        self.setAutoCompletionThreshold(3)
        self.setAutoCompletionSource(QsciScintilla.AcsAPIs)

    def resetPythonPrintStyle(self, lexer):

        self.font = QFont()
        self.font.setFamily(c.getFontFamily())
        self.font.setFixedPitch(True)
        self.font.setPointSize(self.pointSize)
        self.setFont(self.font)

        lexer.setFont(self.font)
        # set Lexer
        self.setLexer(lexer)

        # margins reset

        # Margin 0 is used for line numbers
        fontmetrics = QFontMetrics(self.font)
        self.setMarginsFont(self.font)
        self.setMarginWidth(0, fontmetrics.width("00000") + 5)
        self.setMarginLineNumbers(0, True)
        self.setMarginsBackgroundColor(QColor("#000000"))
        self.setMarginsForegroundColor(QColor("#FFFFFF"))

        # FoldingBox
        self.setFoldMarginColors(QColor('dark green'), QColor('dark green'))
Пример #5
0
class CppEditor(QsciScintilla):
    '''
    classdocs
    '''
    def __init__(self, parent=None, fileName=None, readOnlyFiles=[]):
        '''
        Constructor
        '''
        super(CppEditor, self).__init__(parent)
        self.parent = parent
        self.roFiles = readOnlyFiles

        self.setAcceptDrops(False) # drag&drop is on its parent

        # Set the default font
        font = QtGui.QFont()
        font.setFamily('Courier')
        font.setFixedPitch(True)
        font.setPointSize(10)
        self.setFont(font)
        self.setMarginsFont(font)

        # C/C++ lexer
        self.lexer = QsciLexerCPP(self,  True)
        self.lexer.setDefaultFont(font)
        self.libraryAPIs = QsciAPIs(QsciLexerCPP(self,True))
        self.setLexer(self.lexer)
        #self.SendScintilla(QsciScintilla.SCI_STYLESETFONT, 1, 'Courier')

        # Auto-indent
        self.setTabWidth(4)
        #self.setIndentationsUseTabs(False)
        self.setAutoIndent(True)

        # Current line visible with special background color
        self.setCaretLineVisible(True)
        self.setCaretLineBackgroundColor(QtGui.QColor("#ffe4e4"))

        # Enable brace matching
        self.setBraceMatching(QsciScintilla.SloppyBraceMatch)

        # Enable folding visual- use boxes
        self.setFolding(QsciScintilla.BoxedTreeFoldStyle)

        # show line numbers
        fontmetrics = QtGui.QFontMetrics(font)
        self.setMarginsFont(font)
        self.setMarginWidth(0, fontmetrics.width("00000") + 4)
        self.setMarginLineNumbers(0, True)
        self.setMarginsBackgroundColor(QtGui.QColor("#ccccee"))

        # not too small
        self.setMinimumSize(400, 200)

        # set the length of the string before the editor tries to auto-complete
        self.setAutoCompletionThreshold(3)
        # tell the editor we are using a QsciAPI for the auto-completion
        self.setAutoCompletionSource(QsciScintilla.AcsAPIs)
        # removed remaining right side characters from the current cursor
        self.setAutoCompletionReplaceWord(True)

        # "CTRL+Space" autocomplete
        self.shortcut_ctrl_space = QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+Space"), self)
        self.shortcut_ctrl_space.activated.connect(self.autoCompleteFromAll)

        if fileName:
            self.curFile = fileName
            self.loadFile(fileName)
            self.isUntitled = False
        else:
            self.curFile = PROJECT_NONAME + USER_CODE_EXT
            self.setText( __default_content__ )
            self.isUntitled = True

        self.updateApiKeywords()
        self.isModified = False
        self.textChanged.connect(self.onTextChanged )

    def onTextChanged(self):
        self.isModified = True
        self.parent.onChildContentChanged()

    def loadFile(self, fileName):
        try:
            self.clear()
            with open(fileName, 'r') as f:
                for line in f.readlines():
                    self.append(line)
            return True
        except:
            QtWidgets.QMessageBox.warning(self, PROJECT_ALIAS,
                    "failed to read %s." % fileName )
            return False


    def saveFile(self, fileName):
        if fileName.find(' ')>=0:
            QtWidgets.QMessageBox.warning(self, PROJECT_ALIAS,
                    'File path "%s" contains space(s). Please save to a valid location.'%fileName)
            return None

        try:
            with open(fileName, 'wt') as f:
                f.write(self.text())
        except:
            QtWidgets.QMessageBox.warning(self, PROJECT_ALIAS,
                    "Failed to save %s." % fileName )
            return None

        self.curFile = fileName
        self.isUntitled = False
        self.isModified = False
        return fileName

    def saveAs(self):
        fileName, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save As",
                self.curFile, PROJECT_ALIAS + " (*" + USER_CODE_EXT + ");;" +
                    "C source (*.c);;C++ source (*.cpp);;Text File (*.txt);;All files (*.*)" )
        if not fileName:
            return None
        return self.saveFile(fileName)

    def save(self):
        f1 = os.path.abspath(self.curFile)
        for fname in self.roFiles:
            if f1 == os.path.abspath(fname): # same file
                if QtWidgets.QMessageBox.question(self.parent, "Project is read-only",
                        "This project is marked as \"read-only\".\n" + \
                        "Please click \"Cancel\" and save this to another location.\n\n" + \
                        "Continue saving the current project anyway?", "OK", "Cancel"):
                    return None
                #return self.saveAs()
                return self.saveFile(self.curFile)
        if self.isUntitled:
            return self.saveAs()
        else:
            return self.saveFile(self.curFile)

    def currentFile(self):
        return self.curFile

    def modified(self):
        return self.isModified

    def updateApiKeywords(self):
        self.libraryAPIs.clear()
        self.apiKeywords = self.parent.getDefaultKeywords()
        headerfiles = []
        for line in range(self.lines()):
            txt = str(self.text(line)).strip()
            if txt.find('int') == 0: # e.g. reached "int main()"
                break
            elif txt.find('#include') == 0:
                txt = ''.join(txt.split())
                temp = txt[len('#includes')-1 : ]
                header = temp[1:-1] # get the header file
                hfile = os.path.join('libraries', header[:-2], header)
                if os.path.isfile(hfile):
                    if not (hfile in headerfiles): headerfiles.append( hfile )
        if len( headerfiles ):
            #print 'parsing: ', headerfiles
            self.apiKeywords += getLibraryKeywords( headerfiles )
        #self.apiKeywords = list(set(self.apiKeywords)) # remove duplicates
        for keyword in self.apiKeywords:
            self.libraryAPIs.add( keyword )
        self.libraryAPIs.prepare()
        self.lexer.setAPIs(self.libraryAPIs)

    def insertIncludeDirective(self, library=''):
        directive =  '#include <' + library + '.h>\r\n'
        insert_pos = 0
        found_inc = False
        for line in range(self.lines()):
            txt = str(self.text(line)).strip()
            if txt.find('int') == 0: # e.g. reached "int main()"
                insert_pos = line - 1
                break
            elif txt.find('#include') == 0:
                found_inc = True
            elif found_inc:
                insert_pos = line
                break
        if insert_pos < 0 or insert_pos >= self.lines():
            insert_pos = 0
        self.insertAt(directive, insert_pos, 0)
        self.updateApiKeywords()