Ejemplo n.º 1
0
class Main():
    def __init__(self):
        self.lexer = QsciLexerPython()
        self.apis = QsciAPIs(self.lexer)
        self.lexer.setAPIs(self.apis)
        self.apis.apiPreparationFinished.connect(
            self.slotApiPreparationFinished)
        for fn in API_FILES:
            ok = self.apis.load(PATH + '/api_raw/' + fn)
        self.apis.prepare()

    def slotApiPreparationFinished(self):
        self.apis.savePrepared(PATH + '/prepared.api')
        print('Done.')
        QApplication.quit()
Ejemplo n.º 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, 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 ""
Ejemplo n.º 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,
                 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 ""
Ejemplo n.º 4
0
class PMCodeEditor(QWidget, Ui_FormEditor):
    def __init__(self, *args, **kwargs):
        super(PMCodeEditor, self).__init__(*args, **kwargs)
        self.setupUi(self)
        self._lexer = None
        self._apis = None
        self._init_editor()
        self._init_lexer()
        self._init_apis()
        self._init_signals()

    def _init_editor(self):
        """
        初始化编辑器设置
        Returns:
        """
        self.label_status_ln_col.setText(self.tr('行:1  列:1'))
        self.label_status_length.setText(self.tr('长度:0  行数:1'))
        self.label_status_sel.setText(self.tr('选中:0 | 0'))
        self.textEdit.setContextMenuPolicy(Qt.CustomContextMenu)
        # 设置字体
        self.textEdit.setFont(QFont('Source Code Pro', 12))  # Consolas
        self.textEdit.setMarginsFont(self.textEdit.font())
        # 自动换行
        self.textEdit.setEolMode(QsciScintilla.EolUnix)  # \n换行
        self.textEdit.setWrapMode(QsciScintilla.WrapWord)  # 自动换行
        self.textEdit.setWrapVisualFlags(QsciScintilla.WrapFlagNone)
        self.textEdit.setWrapIndentMode(QsciScintilla.WrapIndentFixed)
        # 编码
        self.textEdit.setUtf8(True)
        self.textEdit.SendScintilla(QsciScintilla.SCI_SETCODEPAGE,
                                    QsciScintilla.SC_CP_UTF8)
        # 自动提示
        self.textEdit.setAnnotationDisplay(
            QsciScintilla.AnnotationBoxed)  # 提示显示方式
        self.textEdit.setAutoCompletionSource(
            QsciScintilla.AcsAll)  # 自动补全。对于所有Ascii字符
        self.textEdit.setAutoCompletionReplaceWord(True)
        self.textEdit.setAutoCompletionCaseSensitivity(False)  # 忽略大小写
        # self.textEdit.setAutoCompletionUseSingle(QsciScintilla.AcusAlways)
        self.textEdit.setAutoCompletionThreshold(1)  # 输入多少个字符才弹出补全提示
        self.textEdit.setCallTipsPosition(
            QsciScintilla.CallTipsBelowText)  # 设置提示位置
        self.textEdit.setCallTipsStyle(
            QsciScintilla.CallTipsNoContext)  # 设置提示样式
        # 设置折叠样式
        self.textEdit.setFolding(
            QsciScintilla.FoldStyle.BoxedTreeFoldStyle)  # 代码折叠
        self.textEdit.setFoldMarginColors(QColor(233, 233, 233), Qt.white)
        # 折叠标签颜色
        self.textEdit.SendScintilla(QsciScintilla.SCI_MARKERSETBACK,
                                    QsciScintilla.SC_MARKNUM_FOLDERSUB,
                                    QColor('0xa0a0a0'))
        self.textEdit.SendScintilla(QsciScintilla.SCI_MARKERSETBACK,
                                    QsciScintilla.SC_MARKNUM_FOLDERMIDTAIL,
                                    QColor('0xa0a0a0'))
        self.textEdit.SendScintilla(QsciScintilla.SCI_MARKERSETBACK,
                                    QsciScintilla.SC_MARKNUM_FOLDERTAIL,
                                    QColor('0xa0a0a0'))
        # 设置当前行背景
        self.textEdit.setCaretLineVisible(True)
        self.textEdit.setCaretLineBackgroundColor(QColor(232, 232, 255))

        # 设置选中文本颜色
        # self.textEdit.setSelectionForegroundColor(QColor(192, 192, 192))
        # self.textEdit.setSelectionBackgroundColor(QColor(192, 192, 192))

        # 括号匹配
        self.textEdit.setBraceMatching(
            QsciScintilla.StrictBraceMatch)  # 大括号严格匹配
        self.textEdit.setMatchedBraceBackgroundColor(Qt.blue)
        self.textEdit.setMatchedBraceForegroundColor(Qt.white)
        self.textEdit.setUnmatchedBraceBackgroundColor(Qt.red)
        self.textEdit.setUnmatchedBraceForegroundColor(Qt.white)

        # 启用活动热点区域的下划线
        self.textEdit.setHotspotUnderline(True)
        self.textEdit.setHotspotWrap(True)

        # 缩进
        self.textEdit.setAutoIndent(True)  # 换行后自动缩进
        self.textEdit.setTabWidth(4)
        self.textEdit.setIndentationWidth(4)
        self.textEdit.setTabIndents(True)
        # 缩进指南
        self.textEdit.setIndentationGuides(True)
        self.textEdit.setIndentationsUseTabs(False)  # 不使用Tab
        self.textEdit.setBackspaceUnindents(True)  # 当一行没有其它字符时删除前面的缩进
        self.textEdit.setIndentationGuidesForegroundColor(QColor(
            192, 192, 192))
        self.textEdit.setIndentationGuidesBackgroundColor(Qt.white)

        # 显示行号
        self.textEdit.setMarginLineNumbers(0, True)
        self.textEdit.setMarginWidth(0, 50)
        self.textEdit.setMarginWidth(1, 0)  # 行号
        #  self.textEdit.setMarginWidth(2, 0)  # 折叠
        self.textEdit.setMarginWidth(3, 0)
        self.textEdit.setMarginWidth(4, 0)
        #  # 折叠区域
        #  self.textEdit.setMarginType(3, QsciScintilla.SymbolMargin)
        #  self.textEdit.setMarginLineNumbers(3, False)
        #  self.textEdit.setMarginWidth(3, 15)
        #  self.textEdit.setMarginSensitivity(3, True)

        # 设置空白字符显示
        self.textEdit.setWhitespaceSize(1)  # 可见的空白点的尺寸
        self.textEdit.setWhitespaceVisibility(
            QsciScintilla.WsVisible)  # 空白的可见性。默认的是空格是无形的
        self.textEdit.setWhitespaceForegroundColor(QColor(255, 181, 106))

    def _init_lexer(self):
        """
        初始化语法解析器
        Returns:
        """
        self._lexer = QsciLexerPython(self.textEdit)
        self._lexer.setFont(self.textEdit.font())
        self.textEdit.setLexer(self._lexer)

    def _init_apis(self):
        """
        加载自定义智能提示文件
        Returns:
        """
        self._apis = QsciAPIs(self._lexer)
        for path in Path(os.path.dirname(__file__)).rglob('*.api'):
            self._apis.load(str(path.absolute()))
        self._apis.prepare()

    def _init_signals(self):
        """
        初始化信号绑定
        Returns:
        """
        # 绑定光标变化信号
        self.textEdit.cursorPositionChanged.connect(
            self.slot_cursor_position_changed)
        # 绑定内容改变信号
        self.textEdit.textChanged.connect(self.slot_text_changed)
        # 绑定选中变化信号
        self.textEdit.selectionChanged.connect(self.slot_selection_changed)
        # 绑定是否被修改信号
        self.textEdit.modificationChanged.connect(
            self.slot_modification_changed)
        # 绑定右键菜单信号
        self.textEdit.customContextMenuRequested.connect(
            self.slot_custom_context_menu_requested)

    def slot_cursor_position_changed(self, line: int, column: int):
        """
        光标变化槽函数
        Args:
            line:
            column:

        Returns:
        """
        self.label_status_ln_col.setText(
            self.tr('行:{0}  列:{1}').format(line + 1, column + 1))

    def slot_text_changed(self):
        """
        内容变化槽函数
        Returns:
        """
        self.label_status_length.setText(
            self.tr('长度:{0}  行数:{1}').format(self.textEdit.length(),
                                             self.textEdit.lines()))

    def slot_selection_changed(self):
        """
        选中内容变化槽函数
        Returns:
        """
        lineFrom, indexFrom, lineTo, indexTo = self.textEdit.getSelection()
        lines = 0 if lineFrom == lineTo == -1 else lineTo - lineFrom + 1
        self.label_status_sel.setText(
            self.tr('选中:{0} | {1}').format(len(self.textEdit.selectedText()),
                                           lines))

    def slot_modification_changed(self, modified: bool):
        """
        内容被修改槽函数
        Args:
            modified:

        Returns:
        """
        title = self.windowTitle()
        if modified:
            if not title.startswith('*'):
                self.setWindowTitle('*' + title)
        else:
            if title.startswith('*'):
                self.setWindowTitle(title[1:])

    def slot_custom_context_menu_requested(self, pos: QPoint):
        """
        右键菜单修改
        Args:
            pos:

        Returns:
        """
        # 创建默认菜单
        menu = self.textEdit.createStandardContextMenu()
        country = QLocale.system().country()
        is_china = QLocale.system().language(
        ) == QLocale.Chinese or country in (QLocale.China, QLocale.HongKong,
                                            QLocale.Taiwan)
        # 遍历本身已有的菜单项做中文翻译处理
        # 前提是要加载了Qt自带的翻译文件
        for action in menu.actions():
            if is_china:
                action.setText(
                    QCoreApplication.translate('QTextControl', action.text()))
        menu.exec_(self.textEdit.mapToGlobal(pos))
        del menu