eric7/DataViews/CodeMetricsDialog.py

branch
eric7
changeset 8312
800c432b34c8
parent 8218
7c09585bd960
child 8318
962bce857696
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2003 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a code metrics dialog.
8 """
9
10 import os
11 import fnmatch
12 import collections
13
14 from PyQt5.QtCore import pyqtSlot, Qt, QLocale
15 from PyQt5.QtWidgets import (
16 QDialog, QDialogButtonBox, QMenu, QHeaderView, QTreeWidgetItem,
17 QApplication
18 )
19
20 from .Ui_CodeMetricsDialog import Ui_CodeMetricsDialog
21 from . import CodeMetrics
22
23 import Utilities
24
25
26 class CodeMetricsDialog(QDialog, Ui_CodeMetricsDialog):
27 """
28 Class implementing a dialog to display the code metrics.
29 """
30 def __init__(self, parent=None):
31 """
32 Constructor
33
34 @param parent parent widget (QWidget)
35 """
36 super().__init__(parent)
37 self.setupUi(self)
38 self.setWindowFlags(Qt.WindowType.Window)
39
40 self.buttonBox.button(
41 QDialogButtonBox.StandardButton.Close).setEnabled(False)
42 self.buttonBox.button(
43 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
44
45 self.summaryList.headerItem().setText(
46 self.summaryList.columnCount(), "")
47 self.summaryList.header().resizeSection(0, 200)
48 self.summaryList.header().resizeSection(1, 100)
49
50 self.resultList.headerItem().setText(self.resultList.columnCount(), "")
51
52 self.cancelled = False
53
54 self.__menu = QMenu(self)
55 self.__menu.addAction(self.tr("Collapse All"),
56 self.__resultCollapse)
57 self.__menu.addAction(self.tr("Expand All"), self.__resultExpand)
58 self.resultList.setContextMenuPolicy(
59 Qt.ContextMenuPolicy.CustomContextMenu)
60 self.resultList.customContextMenuRequested.connect(
61 self.__showContextMenu)
62
63 self.__fileList = []
64 self.__project = None
65 self.filterFrame.setVisible(False)
66
67 def __resizeResultColumns(self):
68 """
69 Private method to resize the list columns.
70 """
71 self.resultList.header().resizeSections(
72 QHeaderView.ResizeMode.ResizeToContents)
73 self.resultList.header().setStretchLastSection(True)
74
75 def __createResultItem(self, parent, values):
76 """
77 Private slot to create a new item in the result list.
78
79 @param parent parent of the new item (QTreeWidget or QTreeWidgetItem)
80 @param values values to be displayed (list)
81 @return the generated item
82 """
83 data = [values[0]]
84 for value in values[1:]:
85 try:
86 data.append("{0:5}".format(int(value)))
87 except ValueError:
88 data.append(value)
89 itm = QTreeWidgetItem(parent, data)
90 for col in range(1, 7):
91 itm.setTextAlignment(
92 col, Qt.Alignment(Qt.AlignmentFlag.AlignRight))
93 return itm
94
95 def __resizeSummaryColumns(self):
96 """
97 Private method to resize the list columns.
98 """
99 self.summaryList.header().resizeSections(
100 QHeaderView.ResizeMode.ResizeToContents)
101 self.summaryList.header().setStretchLastSection(True)
102
103 def __createSummaryItem(self, col0, col1):
104 """
105 Private slot to create a new item in the summary list.
106
107 @param col0 string for column 0 (string)
108 @param col1 string for column 1 (string)
109 """
110 itm = QTreeWidgetItem(self.summaryList, [col0, col1])
111 itm.setTextAlignment(1, Qt.Alignment(Qt.AlignmentFlag.AlignRight))
112
113 def prepare(self, fileList, project):
114 """
115 Public method to prepare the dialog with a list of filenames.
116
117 @param fileList list of filenames (list of strings)
118 @param project reference to the project object (Project)
119 """
120 self.__fileList = fileList[:]
121 self.__project = project
122
123 self.buttonBox.button(
124 QDialogButtonBox.StandardButton.Close).setEnabled(True)
125 self.buttonBox.button(
126 QDialogButtonBox.StandardButton.Cancel).setEnabled(False)
127 self.buttonBox.button(
128 QDialogButtonBox.StandardButton.Close).setDefault(True)
129
130 self.filterFrame.setVisible(True)
131
132 self.__data = self.__project.getData("OTHERTOOLSPARMS", "CodeMetrics")
133 if self.__data is None or "ExcludeFiles" not in self.__data:
134 self.__data = {"ExcludeFiles": ""}
135 self.excludeFilesEdit.setText(self.__data["ExcludeFiles"])
136
137 def start(self, fn):
138 """
139 Public slot to start the code metrics determination.
140
141 @param fn file or list of files or directory to show
142 the code metrics for (string or list of strings)
143 """
144 self.cancelled = False
145 self.buttonBox.button(
146 QDialogButtonBox.StandardButton.Close).setEnabled(False)
147 self.buttonBox.button(
148 QDialogButtonBox.StandardButton.Cancel).setEnabled(True)
149 self.buttonBox.button(
150 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
151 QApplication.processEvents()
152
153 loc = QLocale()
154 if isinstance(fn, list):
155 files = fn
156 elif os.path.isdir(fn):
157 files = Utilities.direntries(fn, True, '*.py', False)
158 else:
159 files = [fn]
160 files.sort()
161 # check for missing files
162 for f in files[:]:
163 if not os.path.exists(f):
164 files.remove(f)
165
166 self.checkProgress.setMaximum(len(files))
167 QApplication.processEvents()
168
169 total = collections.defaultdict(int)
170 CodeMetrics.summarize(total, 'files', len(files))
171
172 try:
173 # disable updates of the list for speed
174 self.resultList.setUpdatesEnabled(False)
175 self.resultList.setSortingEnabled(False)
176
177 # now go through all the files
178 for progress, file in enumerate(files, start=1):
179 if self.cancelled:
180 return
181
182 stats = CodeMetrics.analyze(file, total)
183
184 v = self.__getValues(loc, stats, 'TOTAL ')
185 fitm = self.__createResultItem(self.resultList, [file] + v)
186
187 identifiers = stats.identifiers
188 for identifier in identifiers:
189 v = self.__getValues(loc, stats, identifier)
190
191 self.__createResultItem(fitm, [identifier] + v)
192 self.resultList.expandItem(fitm)
193
194 self.checkProgress.setValue(progress)
195 QApplication.processEvents()
196 finally:
197 # reenable updates of the list
198 self.resultList.setSortingEnabled(True)
199 self.resultList.setUpdatesEnabled(True)
200 self.__resizeResultColumns()
201
202 # now do the summary stuff
203 self.__createSummaryItem(self.tr("files"),
204 loc.toString(total['files']))
205 self.__createSummaryItem(self.tr("lines"),
206 loc.toString(total['lines']))
207 self.__createSummaryItem(self.tr("bytes"),
208 loc.toString(total['bytes']))
209 self.__createSummaryItem(self.tr("comments"),
210 loc.toString(total['comments']))
211 self.__createSummaryItem(self.tr("comment lines"),
212 loc.toString(total['commentlines']))
213 self.__createSummaryItem(self.tr("empty lines"),
214 loc.toString(total['empty lines']))
215 self.__createSummaryItem(self.tr("non-commentary lines"),
216 loc.toString(total['non-commentary lines']))
217 self.__resizeSummaryColumns()
218 self.__finish()
219
220 def __getValues(self, loc, stats, identifier):
221 """
222 Private method to extract the code metric values.
223
224 @param loc reference to the locale object (QLocale)
225 @param stats reference to the code metric statistics object
226 @param identifier identifier to get values for
227 @return list of values suitable for display (list of strings)
228 """
229 counters = stats.counters.get(identifier, {})
230 v = []
231 for key in ('start', 'end', 'lines', 'nloc', 'commentlines', 'empty'):
232 if counters.get(key, 0):
233 v.append(loc.toString(counters[key]))
234 else:
235 v.append('')
236 return v
237
238 def __finish(self):
239 """
240 Private slot called when the action finished or the user pressed the
241 button.
242 """
243 self.cancelled = True
244 self.buttonBox.button(
245 QDialogButtonBox.StandardButton.Close).setEnabled(True)
246 self.buttonBox.button(
247 QDialogButtonBox.StandardButton.Cancel).setEnabled(False)
248 self.buttonBox.button(
249 QDialogButtonBox.StandardButton.Close).setDefault(True)
250
251 self.resultList.header().setSectionResizeMode(
252 QHeaderView.ResizeMode.Interactive)
253 self.summaryList.header().setSectionResizeMode(
254 QHeaderView.ResizeMode.Interactive)
255
256 def on_buttonBox_clicked(self, button):
257 """
258 Private slot called by a button of the button box clicked.
259
260 @param button button that was clicked (QAbstractButton)
261 """
262 if button == self.buttonBox.button(
263 QDialogButtonBox.StandardButton.Close
264 ):
265 self.close()
266 elif button == self.buttonBox.button(
267 QDialogButtonBox.StandardButton.Cancel
268 ):
269 self.__finish()
270
271 @pyqtSlot()
272 def on_startButton_clicked(self):
273 """
274 Private slot to start a code metrics run.
275 """
276 fileList = self.__fileList[:]
277
278 filterString = self.excludeFilesEdit.text()
279 if ("ExcludeFiles" not in self.__data or
280 filterString != self.__data["ExcludeFiles"]):
281 self.__data["ExcludeFiles"] = filterString
282 self.__project.setData("OTHERTOOLSPARMS", "CodeMetrics",
283 self.__data)
284 filterList = filterString.split(",")
285 if filterList:
286 for filterString in filterList:
287 fileList = [f for f in fileList
288 if not fnmatch.fnmatch(f, filterString.strip())]
289
290 self.resultList.clear()
291 self.summaryList.clear()
292 self.start(fileList)
293
294 def __showContextMenu(self, coord):
295 """
296 Private slot to show the context menu of the listview.
297
298 @param coord the position of the mouse pointer (QPoint)
299 """
300 if self.resultList.topLevelItemCount() > 0:
301 self.__menu.popup(self.mapToGlobal(coord))
302
303 def __resultCollapse(self):
304 """
305 Private slot to collapse all entries of the resultlist.
306 """
307 for index in range(self.resultList.topLevelItemCount()):
308 self.resultList.topLevelItem(index).setExpanded(False)
309
310 def __resultExpand(self):
311 """
312 Private slot to expand all entries of the resultlist.
313 """
314 for index in range(self.resultList.topLevelItemCount()):
315 self.resultList.topLevelItem(index).setExpanded(True)

eric ide

mercurial