8 complexity). |
8 complexity). |
9 """ |
9 """ |
10 |
10 |
11 import os |
11 import os |
12 import fnmatch |
12 import fnmatch |
13 import sys |
13 |
14 |
14 from PyQt6.QtCore import pyqtSlot, Qt, QTimer, QLocale |
15 sys.path.insert(0, os.path.dirname(__file__)) |
15 from PyQt6.QtGui import QColor |
16 |
16 from PyQt6.QtWidgets import ( |
17 from PyQt5.QtCore import pyqtSlot, qVersion, Qt, QTimer, QLocale |
|
18 from PyQt5.QtGui import QColor |
|
19 from PyQt5.QtWidgets import ( |
|
20 QDialog, QDialogButtonBox, QAbstractButton, QHeaderView, QTreeWidgetItem, |
17 QDialog, QDialogButtonBox, QAbstractButton, QHeaderView, QTreeWidgetItem, |
21 QApplication, QMenu |
18 QApplication, QMenu |
22 ) |
19 ) |
23 |
20 |
24 from .Ui_CyclomaticComplexityDialog import Ui_CyclomaticComplexityDialog |
21 from .Ui_CyclomaticComplexityDialog import Ui_CyclomaticComplexityDialog |
25 from E5Gui.E5Application import e5App |
22 from EricWidgets.EricApplication import ericApp |
26 |
23 |
27 import Preferences |
24 import Preferences |
28 import Utilities |
25 import Utilities |
29 |
26 |
30 |
27 |
31 class CyclomaticComplexityDialog(QDialog, Ui_CyclomaticComplexityDialog): |
28 class CyclomaticComplexityDialog(QDialog, Ui_CyclomaticComplexityDialog): |
32 """ |
29 """ |
33 Class implementing a dialog to show the cyclomatic complexity (McCabe |
30 Class implementing a dialog to show the cyclomatic complexity (McCabe |
34 complexity). |
31 complexity). |
35 """ |
32 """ |
36 FilePathRole = Qt.UserRole + 1 |
33 FilePathRole = Qt.ItemDataRole.UserRole + 1 |
37 LineNumberRole = Qt.UserRole + 2 |
34 LineNumberRole = Qt.ItemDataRole.UserRole + 2 |
38 |
35 |
39 def __init__(self, radonService, isSingle=False, parent=None): |
36 def __init__(self, radonService, isSingle=False, parent=None): |
40 """ |
37 """ |
41 Constructor |
38 Constructor |
42 |
39 |
47 @param parent reference to the parent widget |
44 @param parent reference to the parent widget |
48 @type QWidget |
45 @type QWidget |
49 """ |
46 """ |
50 super().__init__(parent) |
47 super().__init__(parent) |
51 self.setupUi(self) |
48 self.setupUi(self) |
52 self.setWindowFlags(Qt.Window) |
49 self.setWindowFlags(Qt.WindowType.Window) |
53 |
50 |
54 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) |
51 self.buttonBox.button( |
55 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) |
52 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
|
53 self.buttonBox.button( |
|
54 QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
56 |
55 |
57 self.resultList.headerItem().setText(self.resultList.columnCount(), "") |
56 self.resultList.headerItem().setText(self.resultList.columnCount(), "") |
58 |
57 |
59 self.rankComboBox.addItems(["A", "B", "C", "D", "E", "F"]) |
58 self.rankComboBox.addItems(["A", "B", "C", "D", "E", "F"]) |
60 self.rankComboBox.setCurrentIndex(self.rankComboBox.findText("D")) |
59 self.rankComboBox.setCurrentIndex(self.rankComboBox.findText("D")) |
66 self.radonService.batchFinished.connect(self.__batchFinished) |
65 self.radonService.batchFinished.connect(self.__batchFinished) |
67 |
66 |
68 self.__isSingle = isSingle |
67 self.__isSingle = isSingle |
69 self.cancelled = False |
68 self.cancelled = False |
70 |
69 |
71 self.__project = e5App().getObject("Project") |
70 self.__project = ericApp().getObject("Project") |
72 self.__locale = QLocale() |
71 self.__locale = QLocale() |
73 self.__finished = True |
72 self.__finished = True |
74 self.__errorItem = None |
73 self.__errorItem = None |
75 |
74 |
76 self.__fileList = [] |
75 self.__fileList = [] |
107 "function": "F", |
106 "function": "F", |
108 "method": "M", |
107 "method": "M", |
109 } |
108 } |
110 |
109 |
111 try: |
110 try: |
112 usesDarkPalette = e5App().usesDarkPalette() |
111 usesDarkPalette = ericApp().usesDarkPalette() |
113 except AttributeError: |
112 except AttributeError: |
114 from PyQt5.QtGui import QPalette |
113 from PyQt6.QtGui import QPalette |
115 palette = e5App().palette() |
114 palette = ericApp().palette() |
116 lightness = palette.color(QPalette.Window).lightness() |
115 lightness = palette.color(QPalette.Window).lightness() |
117 usesDarkPalette = lightness <= 128 |
116 usesDarkPalette = lightness <= 128 |
118 if usesDarkPalette: |
117 if usesDarkPalette: |
119 self.__typeColors = { |
118 self.__typeColors = { |
120 "class": QColor("#ffe480"), |
119 "class": QColor("#ffe480"), |
146 |
145 |
147 self.__menu = QMenu(self) |
146 self.__menu = QMenu(self) |
148 self.__menu.addAction(self.tr("Collapse all"), |
147 self.__menu.addAction(self.tr("Collapse all"), |
149 self.__resultCollapse) |
148 self.__resultCollapse) |
150 self.__menu.addAction(self.tr("Expand all"), self.__resultExpand) |
149 self.__menu.addAction(self.tr("Expand all"), self.__resultExpand) |
151 self.resultList.setContextMenuPolicy(Qt.CustomContextMenu) |
150 self.resultList.setContextMenuPolicy( |
|
151 Qt.ContextMenuPolicy.CustomContextMenu) |
152 self.resultList.customContextMenuRequested.connect( |
152 self.resultList.customContextMenuRequested.connect( |
153 self.__showContextMenu) |
153 self.__showContextMenu) |
154 |
154 |
155 def __resizeResultColumns(self): |
155 def __resizeResultColumns(self): |
156 """ |
156 """ |
157 Private method to resize the list columns. |
157 Private method to resize the list columns. |
158 """ |
158 """ |
159 self.resultList.header().resizeSections(QHeaderView.ResizeToContents) |
159 self.resultList.header().resizeSections( |
|
160 QHeaderView.ResizeMode.ResizeToContents) |
160 self.resultList.header().setStretchLastSection(True) |
161 self.resultList.header().setStretchLastSection(True) |
161 |
162 |
162 def __createFileItem(self, filename): |
163 def __createFileItem(self, filename): |
163 """ |
164 """ |
164 Private method to create a new file item in the result list. |
165 Private method to create a new file item in the result list. |
189 values["fullname"], |
190 values["fullname"], |
190 "{0:3}".format(values["complexity"]), |
191 "{0:3}".format(values["complexity"]), |
191 values["rank"], |
192 values["rank"], |
192 "{0:6}".format(values["lineno"]), |
193 "{0:6}".format(values["lineno"]), |
193 ]) |
194 ]) |
194 itm.setTextAlignment(2, Qt.Alignment(Qt.AlignRight)) |
195 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignRight) |
195 itm.setTextAlignment(3, Qt.Alignment(Qt.AlignHCenter)) |
196 itm.setTextAlignment(3, Qt.AlignmentFlag.AlignHCenter) |
196 itm.setTextAlignment(4, Qt.Alignment(Qt.AlignRight)) |
197 itm.setTextAlignment(4, Qt.AlignmentFlag.AlignRight) |
197 if values["rank"] in self.__rankColors: |
198 if values["rank"] in self.__rankColors: |
198 itm.setBackground(3, self.__rankColors[values["rank"]]) |
199 itm.setBackground(3, self.__rankColors[values["rank"]]) |
199 if values["type"] in self.__typeColors: |
200 if values["type"] in self.__typeColors: |
200 itm.setForeground(0, self.__typeColors[values["type"]]) |
201 itm.setForeground(0, self.__typeColors[values["type"]]) |
201 itm.setData(0, self.FilePathRole, |
202 itm.setData(0, self.FilePathRole, |
221 """ |
222 """ |
222 if self.__errorItem is None: |
223 if self.__errorItem is None: |
223 self.__errorItem = QTreeWidgetItem(self.resultList, [ |
224 self.__errorItem = QTreeWidgetItem(self.resultList, [ |
224 self.tr("Errors")]) |
225 self.tr("Errors")]) |
225 self.__errorItem.setExpanded(True) |
226 self.__errorItem.setExpanded(True) |
226 self.__errorItem.setForeground(0, Qt.red) |
227 self.__errorItem.setForeground(0, Qt.GlobalColor.red) |
227 |
228 |
228 msg = "{0} ({1})".format(self.__project.getRelativePath(filename), |
229 msg = "{0} ({1})".format(self.__project.getRelativePath(filename), |
229 message) |
230 message) |
230 if not self.resultList.findItems(msg, Qt.MatchExactly): |
231 if not self.resultList.findItems(msg, Qt.MatchFlag.MatchExactly): |
231 itm = QTreeWidgetItem(self.__errorItem, [msg]) |
232 itm = QTreeWidgetItem(self.__errorItem, [msg]) |
232 itm.setForeground(0, Qt.red) |
233 itm.setForeground(0, Qt.GlobalColor.red) |
233 itm.setFirstColumnSpanned(True) |
234 itm.setFirstColumnSpanned(True) |
234 |
235 |
235 def prepare(self, fileList, project): |
236 def prepare(self, fileList, project): |
236 """ |
237 """ |
237 Public method to prepare the dialog with a list of filenames. |
238 Public method to prepare the dialog with a list of filenames. |
242 @type Project |
243 @type Project |
243 """ |
244 """ |
244 self.__fileList = fileList[:] |
245 self.__fileList = fileList[:] |
245 self.__project = project |
246 self.__project = project |
246 |
247 |
247 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) |
248 self.buttonBox.button( |
248 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) |
249 QDialogButtonBox.StandardButton.Close).setEnabled(True) |
249 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) |
250 self.buttonBox.button( |
|
251 QDialogButtonBox.StandardButton.Cancel).setEnabled(False) |
|
252 self.buttonBox.button( |
|
253 QDialogButtonBox.StandardButton.Close).setDefault(True) |
250 |
254 |
251 self.filterFrame.setVisible(True) |
255 self.filterFrame.setVisible(True) |
252 |
256 |
253 self.__data = self.__project.getData( |
257 self.__data = self.__project.getData( |
254 "OTHERTOOLSPARMS", "RadonCodeMetrics") |
258 "OTHERTOOLSPARMS", "RadonCodeMetrics") |
275 self.__errorItem = None |
279 self.__errorItem = None |
276 self.resultList.clear() |
280 self.resultList.clear() |
277 self.summaryLabel.clear() |
281 self.summaryLabel.clear() |
278 QApplication.processEvents() |
282 QApplication.processEvents() |
279 |
283 |
280 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) |
284 self.buttonBox.button( |
281 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True) |
285 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
282 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) |
286 self.buttonBox.button( |
|
287 QDialogButtonBox.StandardButton.Cancel).setEnabled(True) |
|
288 self.buttonBox.button( |
|
289 QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
283 self.rankComboBox.setEnabled(False) |
290 self.rankComboBox.setEnabled(False) |
284 QApplication.processEvents() |
291 QApplication.processEvents() |
285 |
292 |
286 if isinstance(fn, list): |
293 if isinstance(fn, list): |
287 self.files = fn |
294 self.files = fn |
288 elif os.path.isdir(fn): |
295 elif os.path.isdir(fn): |
289 self.files = [] |
296 self.files = [] |
290 extensions = set(Preferences.getPython("PythonExtensions") + |
297 extensions = set(Preferences.getPython("Python3Extensions")) |
291 Preferences.getPython("Python3Extensions")) |
|
292 for ext in extensions: |
298 for ext in extensions: |
293 self.files.extend( |
299 self.files.extend( |
294 Utilities.direntries(fn, True, '*{0}'.format(ext), 0)) |
300 Utilities.direntries(fn, True, '*{0}'.format(ext), 0)) |
295 else: |
301 else: |
296 self.files = [fn] |
302 self.files = [fn] |
339 Public method to start a cyclomatic complexity calculation for one |
345 Public method to start a cyclomatic complexity calculation for one |
340 Python file. |
346 Python file. |
341 |
347 |
342 The results are reported to the __processResult slot. |
348 The results are reported to the __processResult slot. |
343 |
349 |
344 @keyparam codestring optional sourcestring |
350 @param codestring optional sourcestring |
345 @type str |
351 @type str |
346 """ |
352 """ |
347 if not self.files: |
353 if not self.files: |
348 self.checkProgressLabel.setPath("") |
354 self.checkProgressLabel.setPath("") |
349 self.checkProgress.setMaximum(1) |
355 self.checkProgress.setMaximum(1) |
486 if not self.__finished: |
492 if not self.__finished: |
487 self.__finished = True |
493 self.__finished = True |
488 |
494 |
489 # re-enable updates of the list |
495 # re-enable updates of the list |
490 self.resultList.setSortingEnabled(True) |
496 self.resultList.setSortingEnabled(True) |
491 self.resultList.sortItems(0, Qt.AscendingOrder) |
497 self.resultList.sortItems(0, Qt.SortOrder.AscendingOrder) |
492 self.resultList.sortItems(1, Qt.AscendingOrder) |
498 self.resultList.sortItems(1, Qt.SortOrder.AscendingOrder) |
493 self.resultList.setUpdatesEnabled(True) |
499 self.resultList.setUpdatesEnabled(True) |
494 |
500 |
495 self.cancelled = True |
501 self.cancelled = True |
496 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) |
502 self.buttonBox.button( |
497 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) |
503 QDialogButtonBox.StandardButton.Close).setEnabled(True) |
498 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) |
504 self.buttonBox.button( |
|
505 QDialogButtonBox.StandardButton.Cancel).setEnabled(False) |
|
506 self.buttonBox.button( |
|
507 QDialogButtonBox.StandardButton.Close).setDefault(True) |
499 self.rankComboBox.setEnabled(True) |
508 self.rankComboBox.setEnabled(True) |
500 |
509 |
501 self.resultList.header().resizeSections( |
510 self.resultList.header().resizeSections( |
502 QHeaderView.ResizeToContents) |
511 QHeaderView.ResizeMode.ResizeToContents) |
503 self.resultList.header().setStretchLastSection(True) |
512 self.resultList.header().setStretchLastSection(True) |
504 if qVersion() >= "5.0.0": |
513 self.resultList.header().setSectionResizeMode( |
505 self.resultList.header().setSectionResizeMode( |
514 QHeaderView.ResizeMode.Interactive) |
506 QHeaderView.Interactive) |
|
507 else: |
|
508 self.resultList.header().setResizeMode(QHeaderView.Interactive) |
|
509 |
515 |
510 averageCC = float(self.__ccSum) / (self.__ccCount or 1) |
516 averageCC = float(self.__ccSum) / (self.__ccCount or 1) |
511 |
517 |
512 self.summaryLabel.setText(self.tr( |
518 self.summaryLabel.setText(self.tr( |
513 "<b>Summary:</b><br/>" |
519 "<b>Summary:</b><br/>" |
548 Private slot called by a button of the button box clicked. |
554 Private slot called by a button of the button box clicked. |
549 |
555 |
550 @param button button that was clicked |
556 @param button button that was clicked |
551 @type QAbstractButton |
557 @type QAbstractButton |
552 """ |
558 """ |
553 if button == self.buttonBox.button(QDialogButtonBox.Close): |
559 if button == self.buttonBox.button( |
|
560 QDialogButtonBox.StandardButton.Close |
|
561 ): |
554 self.close() |
562 self.close() |
555 elif button == self.buttonBox.button(QDialogButtonBox.Cancel): |
563 elif button == self.buttonBox.button( |
|
564 QDialogButtonBox.StandardButton.Cancel |
|
565 ): |
556 if self.__batch: |
566 if self.__batch: |
557 self.radonService.cancelComplexityBatch() |
567 self.radonService.cancelComplexityBatch() |
558 QTimer.singleShot(1000, self.__finish) |
568 QTimer.singleShot(1000, self.__finish) |
559 else: |
569 else: |
560 self.__finish() |
570 self.__finish() |
636 @type int |
646 @type int |
637 """ |
647 """ |
638 filename = item.data(0, self.FilePathRole) |
648 filename = item.data(0, self.FilePathRole) |
639 lineno = item.data(0, self.LineNumberRole) |
649 lineno = item.data(0, self.LineNumberRole) |
640 if filename: |
650 if filename: |
641 vm = e5App().getObject("ViewManager") |
651 vm = ericApp().getObject("ViewManager") |
642 vm.openSourceFile(filename, lineno) |
652 vm.openSourceFile(filename, lineno) |
643 |
653 |
644 @pyqtSlot(str) |
654 @pyqtSlot(str) |
645 def on_rankComboBox_activated(self, rank): |
655 def on_rankComboBox_activated(self, rank): |
646 """ |
656 """ |