--- a/PluginMetricsRadon.py Wed Sep 16 18:51:27 2015 +0200 +++ b/PluginMetricsRadon.py Wed Sep 16 19:36:25 2015 +0200 @@ -173,16 +173,22 @@ """ self.__projectRawMetricsAct = None self.__projectRawMetricsDialog = None + self.__projectMIAct = None + self.__projectMIDialog = None self.__projectSeparatorActs = [] self.__projectBrowserMenu = None self.__projectBrowserRawMetricsAct = None self.__projectBrowserRawMetricsDialog = None + self.__projectBrowserMIAct = None + self.__projectBrowserMIDialog = None self.__projectBrowserSeparatorActs = [] self.__editors = [] self.__editorRawMetricsAct = None self.__editorRawMetricsDialog = None + self.__editorMIAct = None + self.__editorMIDialog = None self.__editorSeparatorActs = [] def rawMetrics(self, lang, filename, source): @@ -240,6 +246,62 @@ """ for lang in ['Python2', 'Python3']: self.backgroundService.requestCancel('batch_radon', lang) + + def maintainabilityIndex(self, lang, filename, source): + """ + Public method to prepare maintainability index calculation on one + Python source file. + + @param lang language of the file or None to determine by internal + algorithm + @type str or None + @param filename source filename + @type str + @param source string containing the code + @type str + """ + if lang is None: + lang = 'Python{0}'.format(determinePythonVersion(filename, source)) + if lang not in ['Python2', 'Python3']: + return + + self.backgroundService.enqueueRequest( + 'radon', lang, filename, [source, 'mi']) + + def maintainabilityIndexBatch(self, argumentsList): + """ + Public method to prepare maintainability index calculation on multiple + Python source files. + + @param argumentsList list of arguments tuples with each tuple + containing filename and source + @type (str, str) + """ + data = { + "Python2": [], + "Python3": [], + } + for filename, source in argumentsList: + lang = 'Python{0}'.format(determinePythonVersion(filename, source)) + if lang not in ['Python2', 'Python3']: + continue + else: + data[lang].append((filename, source, 'mi')) + + self.queuedBatches = [] + for lang in ['Python2', 'Python3']: + if data[lang]: + self.queuedBatches.append(lang) + self.backgroundService.enqueueRequest('batch_radon', lang, "", + data[lang]) + self.batchesFinished = False + + def cancelMaintainabilityIndexBatch(self): + """ + Public method to cancel all batch jobs. + """ + for lang in ['Python2', 'Python3']: + self.backgroundService.requestCancel('batch_radon', lang) def activate(self): """ @@ -257,6 +319,7 @@ act = menu.addSeparator() act.setText(self.tr("Radon")) self.__projectSeparatorActs.append(act) + self.__projectRawMetricsAct = E5Action( self.tr('Code Metrics'), self.tr('Code &Metrics...'), 0, 0, @@ -273,11 +336,28 @@ self.__projectRawMetricsAct.triggered.connect( self.__projectRawMetrics) menu.addAction(self.__projectRawMetricsAct) + + self.__projectMIAct = E5Action( + self.tr('Maintainability Index'), + self.tr('Maintainability &Index...'), 0, 0, + self, 'project_show_radon_mi') + self.__projectMIAct.setStatusTip( + self.tr('Show the maintainability index for Python files.')) + self.__projectMIAct.setWhatsThis(self.tr( + """<b>Maintainability Index...</b>""" + """<p>This calculates the maintainability index of Python""" + """ files and shows it together with a ranking.</p>""" + )) + self.__projectMIAct.triggered.connect( + self.__projectMaintainabilityIndex) + menu.addAction(self.__projectMIAct) + act = menu.addSeparator() self.__projectSeparatorActs.append(act) e5App().getObject("Project").addE5Actions([ self.__projectRawMetricsAct, + self.__projectMIAct ]) act = QAction("Radon", self) @@ -300,6 +380,20 @@ )) self.__editorRawMetricsAct.triggered.connect(self.__editorRawMetrics) + self.__editorMIAct = E5Action( + self.tr('Maintainability Index'), + self.tr('Maintainability &Index...'), 0, 0, + self, "") + self.__editorMIAct.setStatusTip( + self.tr('Show the maintainability index for Python files.')) + self.__projectMIAct.setWhatsThis(self.tr( + """<b>Maintainability Index...</b>""" + """<p>This calculates the maintainability index of Python""" + """ files and shows it together with a ranking.</p>""" + )) + self.__editorMIAct.triggered.connect( + self.__editorMaintainabilityIndex) + e5App().getObject("Project").showMenu.connect(self.__projectShowMenu) e5App().getObject("ProjectBrowser").getProjectBrowser("sources")\ .showMenu.connect(self.__projectBrowserShowMenu) @@ -331,8 +425,11 @@ for sep in self.__projectSeparatorActs: menu.removeAction(sep) menu.removeAction(self.__projectRawMetricsAct) - e5App().getObject("Project").removeE5Actions( - [self.__projectRawMetricsAct]) + menu.removeAction(self.__projectMIAct) + e5App().getObject("Project").removeE5Actions([ + self.__projectRawMetricsAct, + self.__projectMIAct + ]) if self.__projectBrowserMenu: for sep in self.__projectBrowserSeparatorActs: @@ -340,6 +437,9 @@ if self.__projectBrowserRawMetricsAct: self.__projectBrowserMenu.removeAction( self.__projectBrowserRawMetricsAct) + if self.__projectBrowserMIAct: + self.__projectBrowserMenu.removeAction( + self.__projectBrowserMIAct) for editor in self.__editors: editor.showMenu.disconnect(self.__editorShowMenu) @@ -348,6 +448,7 @@ for sep in self.__editorSeparatorActs: menu.removeAction(sep) menu.removeAction(self.__editorRawMetricsAct) + menu.removeAction(self.__editorMIAct) self.__initialize() @@ -382,7 +483,7 @@ @type QMenu """ if menuName == "Show": - for act in [self.__projectRawMetricsAct]: + for act in [self.__projectRawMetricsAct, self.__projectMIAct]: if act is not None: act.setEnabled( e5App().getObject("Project").getProjectLanguage() in @@ -421,47 +522,23 @@ self.__projectBrowserRawMetricsAct.triggered.connect( self.__projectBrowserRawMetrics) menu.addAction(self.__projectBrowserRawMetricsAct) - - def __projectRawMetrics(self): - """ - Private slot used to calculate raw code metrics for the project. - """ - project = e5App().getObject("Project") - project.saveAllScripts() - ppath = project.getProjectPath() - files = [os.path.join(ppath, file) - for file in project.pdata["SOURCES"] - if file.endswith( - tuple(Preferences.getPython("Python3Extensions")) + - tuple(Preferences.getPython("PythonExtensions")))] - - from RadonMetrics.RawMetricsDialog import RawMetricsDialog - self.__projectRawMetricsDialog = RawMetricsDialog(self) - self.__projectRawMetricsDialog.show() - self.__projectRawMetricsDialog.prepare(files, project) - - def __projectBrowserRawMetrics(self): - """ - Private method to handle the tabnanny context menu action of the - project sources browser. - """ - browser = e5App().getObject("ProjectBrowser").getProjectBrowser( - "sources") - if browser.getSelectedItemsCount([ProjectBrowserFileItem]) > 1: - fn = [] - for itm in browser.getSelectedItems([ProjectBrowserFileItem]): - fn.append(itm.fileName()) - else: - itm = browser.model().item(browser.currentIndex()) - try: - fn = itm.fileName() - except AttributeError: - fn = itm.dirName() - - from RadonMetrics.RawMetricsDialog import RawMetricsDialog - self.__projectBrowserRawMetricsDialog = RawMetricsDialog(self) - self.__projectBrowserRawMetricsDialog.show() - self.__projectBrowserRawMetricsDialog.start(fn) + + self.__projectBrowserMIAct = E5Action( + self.tr('Maintainability Index'), + self.tr('Maintainability &Index...'), 0, 0, + self, 'project_show_radon_mi') + self.__projectBrowserMIAct.setStatusTip( + self.tr('Show the maintainability index for Python' + ' files.')) + self.__projectBrowserMIAct.setWhatsThis(self.tr( + """<b>Maintainability Index...</b>""" + """<p>This calculates the maintainability index of""" + """ Python files and shows it together with a ranking.""" + """</p>""" + )) + self.__projectBrowserMIAct.triggered.connect( + self.__projectBrowserMaintainabilityIndex) + menu.addAction(self.__projectBrowserMIAct) def __editorOpened(self, editor): """ @@ -474,6 +551,7 @@ if menu is not None: menu.addAction(self.__editorSeparatorActs[0]) menu.addAction(self.__editorRawMetricsAct) + menu.addAction(self.__editorMIAct) menu.addAction(self.__editorSeparatorActs[1]) editor.showMenu.connect(self.__editorShowMenu) self.__editors.append(editor) @@ -499,7 +577,54 @@ @param editor reference to the editor """ if menuName == "Show": - self.__editorRawMetricsAct.setEnabled(editor.isPyFile()) + enable = editor.isPyFile() + self.__editorRawMetricsAct.setEnabled(enable) + self.__editorMIAct.setEnabled(enable) + + ################################################################## + ## Raw code metrics calculations + ################################################################## + + def __projectRawMetrics(self): + """ + Private slot used to calculate raw code metrics for the project. + """ + project = e5App().getObject("Project") + project.saveAllScripts() + ppath = project.getProjectPath() + files = [os.path.join(ppath, file) + for file in project.pdata["SOURCES"] + if file.endswith( + tuple(Preferences.getPython("Python3Extensions")) + + tuple(Preferences.getPython("PythonExtensions")))] + + from RadonMetrics.RawMetricsDialog import RawMetricsDialog + self.__projectRawMetricsDialog = RawMetricsDialog(self) + self.__projectRawMetricsDialog.show() + self.__projectRawMetricsDialog.prepare(files, project) + + def __projectBrowserRawMetrics(self): + """ + Private method to handle the code metrics context menu action of the + project sources browser. + """ + browser = e5App().getObject("ProjectBrowser").getProjectBrowser( + "sources") + if browser.getSelectedItemsCount([ProjectBrowserFileItem]) > 1: + fn = [] + for itm in browser.getSelectedItems([ProjectBrowserFileItem]): + fn.append(itm.fileName()) + else: + itm = browser.model().item(browser.currentIndex()) + try: + fn = itm.fileName() + except AttributeError: + fn = itm.dirName() + + from RadonMetrics.RawMetricsDialog import RawMetricsDialog + self.__projectBrowserRawMetricsDialog = RawMetricsDialog(self) + self.__projectBrowserRawMetricsDialog.show() + self.__projectBrowserRawMetricsDialog.start(fn) def __editorRawMetrics(self): """ @@ -513,3 +638,65 @@ self.__editorRawMetricsDialog = RawMetricsDialog(self) self.__editorRawMetricsDialog.show() self.__editorRawMetricsDialog.start(editor.getFileName()) + + ################################################################## + ## Maintainability index calculations + ################################################################## + + def __projectMaintainabilityIndex(self): + """ + Private slot used to calculate the maintainability indexes for the + project. + """ + project = e5App().getObject("Project") + project.saveAllScripts() + ppath = project.getProjectPath() + files = [os.path.join(ppath, file) + for file in project.pdata["SOURCES"] + if file.endswith( + tuple(Preferences.getPython("Python3Extensions")) + + tuple(Preferences.getPython("PythonExtensions")))] + + from RadonMetrics.MaintainabilityIndexDialog import \ + MaintainabilityIndexDialog + self.__projectMIDialog = MaintainabilityIndexDialog(self) + self.__projectMIDialog.show() + self.__projectMIDialog.prepare(files, project) + + def __projectBrowserMaintainabilityIndex(self): + """ + Private method to handle the maintainability index context menu action + of the project sources browser. + """ + browser = e5App().getObject("ProjectBrowser").getProjectBrowser( + "sources") + if browser.getSelectedItemsCount([ProjectBrowserFileItem]) > 1: + fn = [] + for itm in browser.getSelectedItems([ProjectBrowserFileItem]): + fn.append(itm.fileName()) + else: + itm = browser.model().item(browser.currentIndex()) + try: + fn = itm.fileName() + except AttributeError: + fn = itm.dirName() + + from RadonMetrics.MaintainabilityIndexDialog import \ + MaintainabilityIndexDialog + self.__projectBrowserMIDialog = MaintainabilityIndexDialog(self) + self.__projectBrowserMIDialog.show() + self.__projectBrowserMIDialog.start(fn) + + def __editorMaintainabilityIndex(self): + """ + Private slot to handle the maintainability index action of the editor + show menu. + """ + editor = e5App().getObject("ViewManager").activeWindow() + if editor is not None: + if editor.checkDirty() and editor.getFileName() is not None: + from RadonMetrics.MaintainabilityIndexDialog import \ + MaintainabilityIndexDialog + self.__editorMIDialog = MaintainabilityIndexDialog(self) + self.__editorMIDialog.show() + self.__editorMIDialog.start(editor.getFileName())