153 with contextlib.suppress(KeyError): |
159 with contextlib.suppress(KeyError): |
154 return self.__backgroundColors[testResult.category] |
160 return self.__backgroundColors[testResult.category] |
155 elif role == Qt.ItemDataRole.TextAlignmentRole: |
161 elif role == Qt.ItemDataRole.TextAlignmentRole: |
156 if idx == TopLevelId and column == TestResultsModel.DurationColumn: |
162 if idx == TopLevelId and column == TestResultsModel.DurationColumn: |
157 return Qt.AlignmentFlag.AlignRight.value |
163 return Qt.AlignmentFlag.AlignRight.value |
158 elif role == Qt.ItemDataRole.UserRole: # __IGNORE_WARNING_Y102__ |
164 elif role == Qt.ItemDataRole.UserRole: # __IGNORE_WARNING_Y102__ |
159 if idx == TopLevelId: |
165 if idx == TopLevelId: |
160 testresult = self.__testResults[row] |
166 testresult = self.__testResults[row] |
161 return (testresult.filename, testresult.lineno) |
167 return (testresult.filename, testresult.lineno) |
162 |
168 |
163 return None |
169 return None |
164 |
170 |
165 def headerData(self, section, orientation, |
171 def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole): |
166 role=Qt.ItemDataRole.DisplayRole): |
|
167 """ |
172 """ |
168 Public method to get the header string for the various sections. |
173 Public method to get the header string for the various sections. |
169 |
174 |
170 @param section section number |
175 @param section section number |
171 @type int |
176 @type int |
172 @param orientation orientation of the header |
177 @param orientation orientation of the header |
173 @type Qt.Orientation |
178 @type Qt.Orientation |
174 @param role data role (defaults to Qt.ItemDataRole.DisplayRole) |
179 @param role data role (defaults to Qt.ItemDataRole.DisplayRole) |
175 @type Qt.ItemDataRole (optional) |
180 @type Qt.ItemDataRole (optional) |
176 @return header string of the section |
181 @return header string of the section |
177 @rtype str |
182 @rtype str |
178 """ |
183 """ |
179 if ( |
184 if ( |
180 orientation == Qt.Orientation.Horizontal and |
185 orientation == Qt.Orientation.Horizontal |
181 role == Qt.ItemDataRole.DisplayRole |
186 and role == Qt.ItemDataRole.DisplayRole |
182 ): |
187 ): |
183 return TestResultsModel.Headers[section] |
188 return TestResultsModel.Headers[section] |
184 else: |
189 else: |
185 return None |
190 return None |
186 |
191 |
187 def parent(self, index): |
192 def parent(self, index): |
188 """ |
193 """ |
189 Public method to get the parent of the item pointed to by index. |
194 Public method to get the parent of the item pointed to by index. |
190 |
195 |
191 @param index index of the item |
196 @param index index of the item |
192 @type QModelIndex |
197 @type QModelIndex |
193 @return index of the parent item |
198 @return index of the parent item |
194 @rtype QModelIndex |
199 @rtype QModelIndex |
195 """ |
200 """ |
196 if not index.isValid(): |
201 if not index.isValid(): |
197 return QModelIndex() |
202 return QModelIndex() |
198 |
203 |
199 idx = index.internalId() |
204 idx = index.internalId() |
200 if idx == TopLevelId: |
205 if idx == TopLevelId: |
201 return QModelIndex() |
206 return QModelIndex() |
202 else: |
207 else: |
203 return self.index(idx, 0) |
208 return self.index(idx, 0) |
204 |
209 |
205 def rowCount(self, parent=QModelIndex()): |
210 def rowCount(self, parent=QModelIndex()): |
206 """ |
211 """ |
207 Public method to get the number of row for a given parent index. |
212 Public method to get the number of row for a given parent index. |
208 |
213 |
209 @param parent index of the parent item (defaults to QModelIndex()) |
214 @param parent index of the parent item (defaults to QModelIndex()) |
210 @type QModelIndex (optional) |
215 @type QModelIndex (optional) |
211 @return number of rows |
216 @return number of rows |
212 @rtype int |
217 @rtype int |
213 """ |
218 """ |
214 if not parent.isValid(): |
219 if not parent.isValid(): |
215 return len(self.__testResults) |
220 return len(self.__testResults) |
216 |
221 |
217 if ( |
222 if ( |
218 parent.internalId() == TopLevelId and |
223 parent.internalId() == TopLevelId |
219 parent.column() == 0 and |
224 and parent.column() == 0 |
220 self.__testResults[parent.row()].extra is not None |
225 and self.__testResults[parent.row()].extra is not None |
221 ): |
226 ): |
222 return len(self.__testResults[parent.row()].extra) |
227 return len(self.__testResults[parent.row()].extra) |
223 |
228 |
224 return 0 |
229 return 0 |
225 |
230 |
226 def columnCount(self, parent=QModelIndex()): |
231 def columnCount(self, parent=QModelIndex()): |
227 """ |
232 """ |
228 Public method to get the number of columns. |
233 Public method to get the number of columns. |
229 |
234 |
230 @param parent index of the parent item (defaults to QModelIndex()) |
235 @param parent index of the parent item (defaults to QModelIndex()) |
231 @type QModelIndex (optional) |
236 @type QModelIndex (optional) |
232 @return number of columns |
237 @return number of columns |
233 @rtype int |
238 @rtype int |
234 """ |
239 """ |
235 if not parent.isValid(): |
240 if not parent.isValid(): |
236 return len(TestResultsModel.Headers) |
241 return len(TestResultsModel.Headers) |
237 else: |
242 else: |
238 return 1 |
243 return 1 |
239 |
244 |
240 def clear(self): |
245 def clear(self): |
241 """ |
246 """ |
242 Public method to clear the model data. |
247 Public method to clear the model data. |
243 """ |
248 """ |
244 self.beginResetModel() |
249 self.beginResetModel() |
245 self.__testResults.clear() |
250 self.__testResults.clear() |
246 self.endResetModel() |
251 self.endResetModel() |
247 |
252 |
248 self.summary.emit("") |
253 self.summary.emit("") |
249 |
254 |
250 def sort(self, column, order): |
255 def sort(self, column, order): |
251 """ |
256 """ |
252 Public method to sort the model data by column in order. |
257 Public method to sort the model data by column in order. |
253 |
258 |
254 @param column sort column number |
259 @param column sort column number |
255 @type int |
260 @type int |
256 @param order sort order |
261 @param order sort order |
257 @type Qt.SortOrder |
262 @type Qt.SortOrder |
258 """ # __IGNORE_WARNING_D234r__ |
263 """ # __IGNORE_WARNING_D234r__ |
|
264 |
259 def durationKey(result): |
265 def durationKey(result): |
260 """ |
266 """ |
261 Function to generate a key for duration sorting |
267 Function to generate a key for duration sorting |
262 |
268 |
263 @param result result object |
269 @param result result object |
264 @type TestResult |
270 @type TestResult |
265 @return sort key |
271 @return sort key |
266 @rtype float |
272 @rtype float |
267 """ |
273 """ |
268 return result.duration or -1.0 |
274 return result.duration or -1.0 |
269 |
275 |
270 self.beginResetModel() |
276 self.beginResetModel() |
271 reverse = order == Qt.SortOrder.DescendingOrder |
277 reverse = order == Qt.SortOrder.DescendingOrder |
272 if column == TestResultsModel.StatusColumn: |
278 if column == TestResultsModel.StatusColumn: |
273 self.__testResults.sort(key=attrgetter('category', 'status'), |
279 self.__testResults.sort( |
274 reverse=reverse) |
280 key=attrgetter("category", "status"), reverse=reverse |
|
281 ) |
275 elif column == TestResultsModel.NameColumn: |
282 elif column == TestResultsModel.NameColumn: |
276 self.__testResults.sort(key=attrgetter('name'), reverse=reverse) |
283 self.__testResults.sort(key=attrgetter("name"), reverse=reverse) |
277 elif column == TestResultsModel.MessageColumn: |
284 elif column == TestResultsModel.MessageColumn: |
278 self.__testResults.sort(key=attrgetter('message'), reverse=reverse) |
285 self.__testResults.sort(key=attrgetter("message"), reverse=reverse) |
279 elif column == TestResultsModel.DurationColumn: |
286 elif column == TestResultsModel.DurationColumn: |
280 self.__testResults.sort(key=durationKey, reverse=reverse) |
287 self.__testResults.sort(key=durationKey, reverse=reverse) |
281 self.endResetModel() |
288 self.endResetModel() |
282 |
289 |
283 def getTestResults(self): |
290 def getTestResults(self): |
284 """ |
291 """ |
285 Public method to get the list of test results managed by the model. |
292 Public method to get the list of test results managed by the model. |
286 |
293 |
287 @return list of test results managed by the model |
294 @return list of test results managed by the model |
288 @rtype list of TestResult |
295 @rtype list of TestResult |
289 """ |
296 """ |
290 return copy.deepcopy(self.__testResults) |
297 return copy.deepcopy(self.__testResults) |
291 |
298 |
292 def setTestResults(self, testResults): |
299 def setTestResults(self, testResults): |
293 """ |
300 """ |
294 Public method to set the list of test results of the model. |
301 Public method to set the list of test results of the model. |
295 |
302 |
296 @param testResults test results to be managed by the model |
303 @param testResults test results to be managed by the model |
297 @type list of TestResult |
304 @type list of TestResult |
298 """ |
305 """ |
299 self.beginResetModel() |
306 self.beginResetModel() |
300 self.__testResults = copy.deepcopy(testResults) |
307 self.__testResults = copy.deepcopy(testResults) |
301 self.endResetModel() |
308 self.endResetModel() |
302 |
309 |
303 self.summary.emit(self.__summary()) |
310 self.summary.emit(self.__summary()) |
304 |
311 |
305 def addTestResults(self, testResults): |
312 def addTestResults(self, testResults): |
306 """ |
313 """ |
307 Public method to add test results to the ones already managed by the |
314 Public method to add test results to the ones already managed by the |
308 model. |
315 model. |
309 |
316 |
310 @param testResults test results to be added to the model |
317 @param testResults test results to be added to the model |
311 @type list of TestResult |
318 @type list of TestResult |
312 """ |
319 """ |
313 firstRow = len(self.__testResults) |
320 firstRow = len(self.__testResults) |
314 lastRow = firstRow + len(testResults) - 1 |
321 lastRow = firstRow + len(testResults) - 1 |
315 self.beginInsertRows(QModelIndex(), firstRow, lastRow) |
322 self.beginInsertRows(QModelIndex(), firstRow, lastRow) |
316 self.__testResults.extend(testResults) |
323 self.__testResults.extend(testResults) |
317 self.endInsertRows() |
324 self.endInsertRows() |
318 |
325 |
319 self.summary.emit(self.__summary()) |
326 self.summary.emit(self.__summary()) |
320 |
327 |
321 def updateTestResults(self, testResults): |
328 def updateTestResults(self, testResults): |
322 """ |
329 """ |
323 Public method to update the data of managed test result items. |
330 Public method to update the data of managed test result items. |
324 |
331 |
325 @param testResults test results to be updated |
332 @param testResults test results to be updated |
326 @type list of TestResult |
333 @type list of TestResult |
327 """ |
334 """ |
328 minIndex = None |
335 minIndex = None |
329 maxIndex = None |
336 maxIndex = None |
330 |
337 |
331 testResultsToBeAdded = [] |
338 testResultsToBeAdded = [] |
332 |
339 |
333 for testResult in testResults: |
340 for testResult in testResults: |
334 for (index, currentResult) in enumerate(self.__testResults): |
341 for (index, currentResult) in enumerate(self.__testResults): |
335 if currentResult.id == testResult.id: |
342 if currentResult.id == testResult.id: |
336 self.__testResults[index] = testResult |
343 self.__testResults[index] = testResult |
337 if minIndex is None: |
344 if minIndex is None: |
338 minIndex = index |
345 minIndex = index |
339 maxIndex = index |
346 maxIndex = index |
340 else: |
347 else: |
341 minIndex = min(minIndex, index) |
348 minIndex = min(minIndex, index) |
342 maxIndex = max(maxIndex, index) |
349 maxIndex = max(maxIndex, index) |
343 |
350 |
344 break |
351 break |
345 else: |
352 else: |
346 # Test result with given id was not found. |
353 # Test result with given id was not found. |
347 # Just add it to the list (could be a sub test) |
354 # Just add it to the list (could be a sub test) |
348 testResultsToBeAdded.append(testResult) |
355 testResultsToBeAdded.append(testResult) |
349 |
356 |
350 if minIndex is not None: |
357 if minIndex is not None: |
351 self.dataChanged.emit( |
358 self.dataChanged.emit( |
352 self.index(minIndex, 0), |
359 self.index(minIndex, 0), |
353 self.index(maxIndex, len(TestResultsModel.Headers) - 1) |
360 self.index(maxIndex, len(TestResultsModel.Headers) - 1), |
354 ) |
361 ) |
355 |
362 |
356 self.summary.emit(self.__summary()) |
363 self.summary.emit(self.__summary()) |
357 |
364 |
358 if testResultsToBeAdded: |
365 if testResultsToBeAdded: |
359 self.addTestResults(testResultsToBeAdded) |
366 self.addTestResults(testResultsToBeAdded) |
360 |
367 |
361 def getFailedTests(self): |
368 def getFailedTests(self): |
362 """ |
369 """ |
363 Public method to extract the test ids of all failed tests. |
370 Public method to extract the test ids of all failed tests. |
364 |
371 |
365 @return test ids of all failed tests |
372 @return test ids of all failed tests |
366 @rtype list of str |
373 @rtype list of str |
367 """ |
374 """ |
368 failedIds = [ |
375 failedIds = [ |
369 res.id for res in self.__testResults if ( |
376 res.id |
370 res.category == TestResultCategory.FAIL and |
377 for res in self.__testResults |
371 not res.subtestResult |
378 if (res.category == TestResultCategory.FAIL and not res.subtestResult) |
372 ) |
|
373 ] |
379 ] |
374 return failedIds |
380 return failedIds |
375 |
381 |
376 def __summary(self): |
382 def __summary(self): |
377 """ |
383 """ |
378 Private method to generate a test results summary text. |
384 Private method to generate a test results summary text. |
379 |
385 |
380 @return test results summary text |
386 @return test results summary text |
381 @rtype str |
387 @rtype str |
382 """ |
388 """ |
383 if len(self.__testResults) == 0: |
389 if len(self.__testResults) == 0: |
384 return self.tr("No results to show") |
390 return self.tr("No results to show") |
385 |
391 |
386 counts = Counter(res.category for res in self.__testResults) |
392 counts = Counter(res.category for res in self.__testResults) |
387 if all( |
393 if all( |
388 counts[category] == 0 |
394 counts[category] == 0 |
389 for category in (TestResultCategory.FAIL, TestResultCategory.OK, |
395 for category in ( |
390 TestResultCategory.SKIP) |
396 TestResultCategory.FAIL, |
|
397 TestResultCategory.OK, |
|
398 TestResultCategory.SKIP, |
|
399 ) |
391 ): |
400 ): |
392 return self.tr("Collected %n test(s)", "", len(self.__testResults)) |
401 return self.tr("Collected %n test(s)", "", len(self.__testResults)) |
393 |
402 |
394 return self.tr( |
403 return self.tr( |
395 "%n test(s)/subtest(s) total, {0} failed, {1} passed," |
404 "%n test(s)/subtest(s) total, {0} failed, {1} passed," |
396 " {2} skipped, {3} pending", |
405 " {2} skipped, {3} pending", |
397 "", len(self.__testResults) |
406 "", |
|
407 len(self.__testResults), |
398 ).format( |
408 ).format( |
399 counts[TestResultCategory.FAIL], |
409 counts[TestResultCategory.FAIL], |
400 counts[TestResultCategory.OK], |
410 counts[TestResultCategory.OK], |
401 counts[TestResultCategory.SKIP], |
411 counts[TestResultCategory.SKIP], |
402 counts[TestResultCategory.PENDING] |
412 counts[TestResultCategory.PENDING], |
403 ) |
413 ) |
404 |
414 |
405 |
415 |
406 class TestResultsTreeView(QTreeView): |
416 class TestResultsTreeView(QTreeView): |
407 """ |
417 """ |
408 Class implementing a tree view to show the test result data. |
418 Class implementing a tree view to show the test result data. |
409 |
419 |
410 @signal goto(str, int) emitted to go to the position given by file name |
420 @signal goto(str, int) emitted to go to the position given by file name |
411 and line number |
421 and line number |
412 """ |
422 """ |
|
423 |
413 goto = pyqtSignal(str, int) |
424 goto = pyqtSignal(str, int) |
414 |
425 |
415 def __init__(self, parent=None): |
426 def __init__(self, parent=None): |
416 """ |
427 """ |
417 Constructor |
428 Constructor |
418 |
429 |
419 @param parent reference to the parent widget (defaults to None) |
430 @param parent reference to the parent widget (defaults to None) |
420 @type QWidget (optional) |
431 @type QWidget (optional) |
421 """ |
432 """ |
422 super().__init__(parent) |
433 super().__init__(parent) |
423 |
434 |
424 self.setItemsExpandable(True) |
435 self.setItemsExpandable(True) |
425 self.setExpandsOnDoubleClick(False) |
436 self.setExpandsOnDoubleClick(False) |
426 self.setSortingEnabled(True) |
437 self.setSortingEnabled(True) |
427 |
438 |
428 self.header().setDefaultAlignment(Qt.AlignmentFlag.AlignCenter) |
439 self.header().setDefaultAlignment(Qt.AlignmentFlag.AlignCenter) |
429 self.header().setSortIndicatorShown(False) |
440 self.header().setSortIndicatorShown(False) |
430 |
441 |
431 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) |
442 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) |
432 |
443 |
433 # connect signals and slots |
444 # connect signals and slots |
434 self.doubleClicked.connect(self.__gotoTestDefinition) |
445 self.doubleClicked.connect(self.__gotoTestDefinition) |
435 self.customContextMenuRequested.connect(self.__showContextMenu) |
446 self.customContextMenuRequested.connect(self.__showContextMenu) |
436 |
447 |
437 self.header().sortIndicatorChanged.connect(self.sortByColumn) |
448 self.header().sortIndicatorChanged.connect(self.sortByColumn) |
438 self.header().sortIndicatorChanged.connect( |
449 self.header().sortIndicatorChanged.connect( |
439 lambda column, order: self.header().setSortIndicatorShown(True)) |
450 lambda column, order: self.header().setSortIndicatorShown(True) |
440 |
451 ) |
|
452 |
441 def reset(self): |
453 def reset(self): |
442 """ |
454 """ |
443 Public method to reset the internal state of the view. |
455 Public method to reset the internal state of the view. |
444 """ |
456 """ |
445 super().reset() |
457 super().reset() |
446 |
458 |
447 self.resizeColumns() |
459 self.resizeColumns() |
448 self.spanFirstColumn(0, self.model().rowCount() - 1) |
460 self.spanFirstColumn(0, self.model().rowCount() - 1) |
449 |
461 |
450 def rowsInserted(self, parent, startRow, endRow): |
462 def rowsInserted(self, parent, startRow, endRow): |
451 """ |
463 """ |
452 Public method called when rows are inserted. |
464 Public method called when rows are inserted. |
453 |
465 |
454 @param parent model index of the parent item |
466 @param parent model index of the parent item |
455 @type QModelIndex |
467 @type QModelIndex |
456 @param startRow first row been inserted |
468 @param startRow first row been inserted |
457 @type int |
469 @type int |
458 @param endRow last row been inserted |
470 @param endRow last row been inserted |
459 @type int |
471 @type int |
460 """ |
472 """ |
461 super().rowsInserted(parent, startRow, endRow) |
473 super().rowsInserted(parent, startRow, endRow) |
462 |
474 |
463 self.resizeColumns() |
475 self.resizeColumns() |
464 self.spanFirstColumn(startRow, endRow) |
476 self.spanFirstColumn(startRow, endRow) |
465 |
477 |
466 def dataChanged(self, topLeft, bottomRight, roles=[]): |
478 def dataChanged(self, topLeft, bottomRight, roles=[]): |
467 """ |
479 """ |
468 Public method called when the model data has changed. |
480 Public method called when the model data has changed. |
469 |
481 |
470 @param topLeft index of the top left element |
482 @param topLeft index of the top left element |
471 @type QModelIndex |
483 @type QModelIndex |
472 @param bottomRight index of the bottom right element |
484 @param bottomRight index of the bottom right element |
473 @type QModelIndex |
485 @type QModelIndex |
474 @param roles list of roles changed (defaults to []) |
486 @param roles list of roles changed (defaults to []) |
475 @type list of Qt.ItemDataRole (optional) |
487 @type list of Qt.ItemDataRole (optional) |
476 """ |
488 """ |
477 super().dataChanged(topLeft, bottomRight, roles) |
489 super().dataChanged(topLeft, bottomRight, roles) |
478 |
490 |
479 self.resizeColumns() |
491 self.resizeColumns() |
480 while topLeft.parent().isValid(): |
492 while topLeft.parent().isValid(): |
481 topLeft = topLeft.parent() |
493 topLeft = topLeft.parent() |
482 while bottomRight.parent().isValid(): |
494 while bottomRight.parent().isValid(): |
483 bottomRight = bottomRight.parent() |
495 bottomRight = bottomRight.parent() |
484 self.spanFirstColumn(topLeft.row(), bottomRight.row()) |
496 self.spanFirstColumn(topLeft.row(), bottomRight.row()) |
485 |
497 |
486 def resizeColumns(self): |
498 def resizeColumns(self): |
487 """ |
499 """ |
488 Public method to resize the columns to their contents. |
500 Public method to resize the columns to their contents. |
489 """ |
501 """ |
490 for column in range(self.model().columnCount()): |
502 for column in range(self.model().columnCount()): |
491 self.resizeColumnToContents(column) |
503 self.resizeColumnToContents(column) |
492 |
504 |
493 def spanFirstColumn(self, startRow, endRow): |
505 def spanFirstColumn(self, startRow, endRow): |
494 """ |
506 """ |
495 Public method to make the first column span the row for second level |
507 Public method to make the first column span the row for second level |
496 items. |
508 items. |
497 |
509 |
498 These items contain the test results. |
510 These items contain the test results. |
499 |
511 |
500 @param startRow index of the first row to span |
512 @param startRow index of the first row to span |
501 @type QModelIndex |
513 @type QModelIndex |
502 @param endRow index of the last row (including) to span |
514 @param endRow index of the last row (including) to span |
503 @type QModelIndex |
515 @type QModelIndex |
504 """ |
516 """ |
505 model = self.model() |
517 model = self.model() |
506 for row in range(startRow, endRow + 1): |
518 for row in range(startRow, endRow + 1): |
507 index = model.index(row, 0) |
519 index = model.index(row, 0) |
508 for i in range(model.rowCount(index)): |
520 for i in range(model.rowCount(index)): |
509 self.setFirstColumnSpanned(i, index, True) |
521 self.setFirstColumnSpanned(i, index, True) |
510 |
522 |
511 def __canonicalIndex(self, index): |
523 def __canonicalIndex(self, index): |
512 """ |
524 """ |
513 Private method to create the canonical index for a given index. |
525 Private method to create the canonical index for a given index. |
514 |
526 |
515 The canonical index is the index of the first column of the test |
527 The canonical index is the index of the first column of the test |
516 result entry (i.e. the top-level item). If the index is invalid, |
528 result entry (i.e. the top-level item). If the index is invalid, |
517 None is returned. |
529 None is returned. |
518 |
530 |
519 @param index index to determine the canonical index for |
531 @param index index to determine the canonical index for |
520 @type QModelIndex |
532 @type QModelIndex |
521 @return index of the firt column of the associated top-level item index |
533 @return index of the firt column of the associated top-level item index |
522 @rtype QModelIndex |
534 @rtype QModelIndex |
523 """ |
535 """ |
524 if not index.isValid(): |
536 if not index.isValid(): |
525 return None |
537 return None |
526 |
538 |
527 while index.parent().isValid(): # find the top-level node |
539 while index.parent().isValid(): # find the top-level node |
528 index = index.parent() |
540 index = index.parent() |
529 index = index.sibling(index.row(), 0) # go to first column |
541 index = index.sibling(index.row(), 0) # go to first column |
530 return index |
542 return index |
531 |
543 |
532 @pyqtSlot(QModelIndex) |
544 @pyqtSlot(QModelIndex) |
533 def __gotoTestDefinition(self, index): |
545 def __gotoTestDefinition(self, index): |
534 """ |
546 """ |
535 Private slot to show the test definition. |
547 Private slot to show the test definition. |
536 |
548 |
537 @param index index for the double-clicked item |
549 @param index index for the double-clicked item |
538 @type QModelIndex |
550 @type QModelIndex |
539 """ |
551 """ |
540 cindex = self.__canonicalIndex(index) |
552 cindex = self.__canonicalIndex(index) |
541 filename, lineno = self.model().data(cindex, Qt.ItemDataRole.UserRole) |
553 filename, lineno = self.model().data(cindex, Qt.ItemDataRole.UserRole) |
542 if filename is not None: |
554 if filename is not None: |
543 if lineno is None: |
555 if lineno is None: |
544 lineno = 1 |
556 lineno = 1 |
545 self.goto.emit(filename, lineno) |
557 self.goto.emit(filename, lineno) |
546 |
558 |
547 @pyqtSlot(QPoint) |
559 @pyqtSlot(QPoint) |
548 def __showContextMenu(self, pos): |
560 def __showContextMenu(self, pos): |
549 """ |
561 """ |
550 Private slot to show the context menu. |
562 Private slot to show the context menu. |
551 |
563 |
552 @param pos relative position for the context menu |
564 @param pos relative position for the context menu |
553 @type QPoint |
565 @type QPoint |
554 """ |
566 """ |
555 index = self.indexAt(pos) |
567 index = self.indexAt(pos) |
556 cindex = self.__canonicalIndex(index) |
568 cindex = self.__canonicalIndex(index) |
557 |
569 |
558 contextMenu = ( |
570 contextMenu = ( |
559 self.__createContextMenu(cindex) |
571 self.__createContextMenu(cindex) |
560 if cindex else |
572 if cindex |
561 self.__createBackgroundContextMenu() |
573 else self.__createBackgroundContextMenu() |
562 ) |
574 ) |
563 contextMenu.exec(self.mapToGlobal(pos)) |
575 contextMenu.exec(self.mapToGlobal(pos)) |
564 |
576 |
565 def __createContextMenu(self, index): |
577 def __createContextMenu(self, index): |
566 """ |
578 """ |
567 Private method to create a context menu for the item pointed to by the |
579 Private method to create a context menu for the item pointed to by the |
568 given index. |
580 given index. |
569 |
581 |
570 @param index index of the item |
582 @param index index of the item |
571 @type QModelIndex |
583 @type QModelIndex |
572 @return created context menu |
584 @return created context menu |
573 @rtype QMenu |
585 @rtype QMenu |
574 """ |
586 """ |
575 menu = QMenu(self) |
587 menu = QMenu(self) |
576 if self.isExpanded(index): |
588 if self.isExpanded(index): |
577 menu.addAction(self.tr("Collapse"), |
589 menu.addAction(self.tr("Collapse"), lambda: self.collapse(index)) |
578 lambda: self.collapse(index)) |
|
579 else: |
590 else: |
580 act = menu.addAction(self.tr("Expand"), |
591 act = menu.addAction(self.tr("Expand"), lambda: self.expand(index)) |
581 lambda: self.expand(index)) |
|
582 act.setEnabled(self.model().hasChildren(index)) |
592 act.setEnabled(self.model().hasChildren(index)) |
583 menu.addSeparator() |
593 menu.addSeparator() |
584 |
594 |
585 act = menu.addAction(self.tr("Show Source"), |
595 act = menu.addAction( |
586 lambda: self.__gotoTestDefinition(index)) |
596 self.tr("Show Source"), lambda: self.__gotoTestDefinition(index) |
587 act.setEnabled( |
|
588 self.model().data(index, Qt.ItemDataRole.UserRole) is not None |
|
589 ) |
597 ) |
|
598 act.setEnabled(self.model().data(index, Qt.ItemDataRole.UserRole) is not None) |
590 menu.addSeparator() |
599 menu.addSeparator() |
591 |
600 |
592 menu.addAction(self.tr("Collapse All"), self.collapseAll) |
601 menu.addAction(self.tr("Collapse All"), self.collapseAll) |
593 menu.addAction(self.tr("Expand All"), self.expandAll) |
602 menu.addAction(self.tr("Expand All"), self.expandAll) |
594 |
603 |
595 return menu |
604 return menu |
596 |
605 |
597 def __createBackgroundContextMenu(self): |
606 def __createBackgroundContextMenu(self): |
598 """ |
607 """ |
599 Private method to create a context menu for the background. |
608 Private method to create a context menu for the background. |
600 |
609 |
601 @return created context menu |
610 @return created context menu |
602 @rtype QMenu |
611 @rtype QMenu |
603 """ |
612 """ |
604 menu = QMenu(self) |
613 menu = QMenu(self) |
605 menu.addAction(self.tr("Collapse All"), self.collapseAll) |
614 menu.addAction(self.tr("Collapse All"), self.collapseAll) |
606 menu.addAction(self.tr("Expand All"), self.expandAll) |
615 menu.addAction(self.tr("Expand All"), self.expandAll) |
607 |
616 |
608 return menu |
617 return menu |
|
618 |
609 |
619 |
610 # |
620 # |
611 # eflag: noqa = M821, M822 |
621 # eflag: noqa = M821, M822 |