eric6/DataViews/CodeMetricsDialog.py

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

eric ide

mercurial