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