--- a/RadonMetrics/RawMetricsDialog.py Tue Sep 15 19:22:38 2015 +0200 +++ b/RadonMetrics/RawMetricsDialog.py Wed Sep 16 18:51:27 2015 +0200 @@ -9,13 +9,18 @@ from __future__ import unicode_literals +try: + str = unicode # __IGNORE_EXCEPTION __IGNORE_WARNING__ +except NameError: + pass + import os import fnmatch -from PyQt5.QtCore import pyqtSlot, qVersion, Qt, QTimer +from PyQt5.QtCore import pyqtSlot, qVersion, Qt, QTimer, QLocale from PyQt5.QtWidgets import ( - QDialog, QDialogButtonBox, QAbstractButton, QMenu, QHeaderView, - QTreeWidgetItem, QApplication + QDialog, QDialogButtonBox, QAbstractButton, QHeaderView, QTreeWidgetItem, + QApplication ) from .Ui_RawMetricsDialog import Ui_RawMetricsDialog @@ -25,8 +30,6 @@ import Preferences import Utilities -# TODO: add summary table -# TODO: add column explanations class RawMetricsDialog(QDialog, Ui_RawMetricsDialog): """ Class implementing a dialog to show raw code metrics. @@ -47,6 +50,11 @@ self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) + self.summaryList.headerItem().setText( + self.summaryList.columnCount(), "") + self.summaryList.header().resizeSection(0, 200) + self.summaryList.header().resizeSection(1, 100) + self.resultList.headerItem().setText(self.resultList.columnCount(), "") self.radonService = radonService @@ -57,17 +65,29 @@ self.cancelled = False self.__project = e5App().getObject("Project") - - self.__menu = QMenu(self) - self.__menu.addAction(self.tr("Collapse all"), - self.__resultCollapse) - self.__menu.addAction(self.tr("Expand all"), self.__resultExpand) - self.resultList.setContextMenuPolicy(Qt.CustomContextMenu) - self.resultList.customContextMenuRequested.connect( - self.__showContextMenu) + self.__locale = QLocale() self.__fileList = [] self.filterFrame.setVisible(False) + + self.explanationLabel.setText(self.tr( + "<table>" + "<tr><td><b>LOC</b></td>" + "<td>Lines of code (LOC = SLOC + Empty)</td></tr>" + "<tr><td><b>SLOC</b></td><td>Source lines of code</td></tr>" + "<tr><td><b>LLOC</b></td><td>Logical lines of code</td></tr>" + "<tr><td><b>Comments</b></td><td>Comment lines</td></tr>" + "<tr><td><b>Multi</b></td>" + "<td>Lines in multi line strings</td></tr>" + "<tr><td><b>Empty</b></td><td>Blank lines</td></tr>" + "<tr><td colspan=2><b>Comment Statistics:</b></td</tr>" + "<tr><td><b>C % L</b></td><td>Comments to lines ratio</td></tr>" + "<tr><td><b>C % S</b></td>" + "<td>Comments to source lines ratio</td></tr>" + "<tr><td><b>C + M % L</b></td>" + "<td>Comments plus multi line strings to lines ratio</td></tr>" + "</table>" + )) def __resizeResultColumns(self): """ @@ -86,13 +106,11 @@ @type dict """ data = [self.__project.getRelativePath(filename)] - for key in ['loc', 'sloc', 'lloc', 'comments', 'multi', 'blank']: + for value in self.__getValues(values): try: - data.append("{0:5}".format(int(values[key]))) + data.append("{0:5}".format(int(value))) except ValueError: - data.append(values[key]) - except KeyError: - data.append("") + data.append(value) data.append("{0:3.0%}".format( values["comments"] / (float(values["loc"]) or 1))) data.append("{0:3.0%}".format( @@ -113,7 +131,13 @@ @param message error message @type str """ - # TODO: implement this + itm = QTreeWidgetItem(self.resultList, [ + "{0} ({1})".format(self.__project.getRelativePath(filename), + message)]) + itm.setFirstColumnSpanned(True) + font = itm.font(0) + font.setItalic(True) + itm.setFont(0, font) def prepare(self, fileList, project): """ @@ -169,6 +193,10 @@ if not os.path.exists(f): self.files.remove(f) + self.__summary = {"files": 0} + for key in ['loc', 'sloc', 'lloc', 'comments', 'multi', 'blank']: + self.__summary[key] = 0 + if len(self.files) > 0: # disable updates of the list for speed self.resultList.setUpdatesEnabled(False) @@ -217,10 +245,7 @@ self.source = Utilities.readEncodedFile(self.filename)[0] self.source = Utilities.normalizeCode(self.source) except (UnicodeError, IOError) as msg: - # TODO: adjust this - self.__createResultItem( - self.filename, 1, - "Error: {0}".format(str(msg)).rstrip()) + self.__createErrorItem(self.filename, str(msg).rstrip()) self.progress += 1 # Continue with next file self.rawMetrics() @@ -251,10 +276,7 @@ source = Utilities.readEncodedFile(filename)[0] source = Utilities.normalizeCode(source) except (UnicodeError, IOError) as msg: - # TODO: adjust this - self.__createResultItem( - filename, 1, - "Error: {0}".format(str(msg)).rstrip()) + self.__createErrorItem(filename, str(msg).rstrip()) continue argumentsList.append((filename, source)) @@ -276,8 +298,15 @@ self.__finish() def __processError(self, fn, msg): - # TODO: implement this - print("Error", fn, msg) + """ + Private slot to process an error indication from the service. + + @param fn filename of the file + @type str + @param msg error message + @type str + """ + self.__createErrorItem(fn, msg) def __processResult(self, fn, result): """ @@ -310,7 +339,27 @@ if not self.__batch: self.rawMetrics() - + + def __getValues(self, result): + """ + Private method to extract the code metric values. + + @param result result dict + @type dict + @return list of values suitable for display + @rtype list of str + """ + v = [] + for key in ['loc', 'sloc', 'lloc', 'comments', 'multi', 'blank']: + val = result.get(key, 0) + if val: + v.append(self.__locale.toString(val)) + else: + v.append('') + self.__summary[key] += int(val) + self.__summary["files"] += 1 + return v + def __finish(self): """ Private slot called when the action or the user pressed the button. @@ -322,6 +371,8 @@ self.resultList.setSortingEnabled(True) self.resultList.setUpdatesEnabled(True) + self.__createSummary() + self.cancelled = True self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) @@ -339,6 +390,39 @@ self.checkProgress.setVisible(False) self.checkProgressLabel.setVisible(False) + def __createSummary(self): + """ + Private method to create the code metrics summary. + """ + self.__createSummaryItem( + self.tr("Files"), self.__locale.toString(self.__summary["files"])) + self.__createSummaryItem( + self.tr("LOC"), self.__locale.toString(self.__summary["loc"])) + self.__createSummaryItem( + self.tr("SLOC"), self.__locale.toString(self.__summary["sloc"])) + self.__createSummaryItem( + self.tr("LLOC"), self.__locale.toString(self.__summary["lloc"])) + self.__createSummaryItem( + self.tr("Comments"), + self.__locale.toString(self.__summary["comments"])) + self.__createSummaryItem( + self.tr("Multi"), self.__locale.toString(self.__summary["multi"])) + self.__createSummaryItem( + self.tr("Empty"), self.__locale.toString(self.__summary["blank"])) + + self.summaryList.header().resizeSections(QHeaderView.ResizeToContents) + self.summaryList.header().setStretchLastSection(True) + + def __createSummaryItem(self, col0, col1): + """ + Private slot to create a new item in the summary list. + + @param col0 string for column 0 (string) + @param col1 string for column 1 (string) + """ + itm = QTreeWidgetItem(self.summaryList, [col0, col1]) + itm.setTextAlignment(1, Qt.Alignment(Qt.AlignRight)) + @pyqtSlot(QAbstractButton) def on_buttonBox_clicked(self, button): """ @@ -379,27 +463,3 @@ self.resultList.clear() self.cancelled = False self.start(fileList) - - def __showContextMenu(self, coord): - """ - Private slot to show the context menu of the result list. - - @param coord position of the mouse pointer - @type QPoint - """ - if self.resultList.topLevelItemCount() > 0: - self.__menu.popup(self.mapToGlobal(coord)) - - def __resultCollapse(self): - """ - Private slot to collapse all entries of the result list. - """ - for index in range(self.resultList.topLevelItemCount()): - self.resultList.topLevelItem(index).setExpanded(False) - - def __resultExpand(self): - """ - Private slot to expand all entries of the result list. - """ - for index in range(self.resultList.topLevelItemCount()): - self.resultList.topLevelItem(index).setExpanded(True)