eric6/Debugger/BreakPointViewer.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) 2005 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the Breakpoint viewer widget.
8 """
9
10 from __future__ import unicode_literals
11
12 from PyQt5.QtCore import pyqtSignal, Qt, QItemSelectionModel, \
13 QSortFilterProxyModel, QFileInfo
14 from PyQt5.QtWidgets import QTreeView, QAbstractItemView, QHeaderView, QMenu, \
15 QDialog
16
17 from E5Gui.E5Application import e5App
18
19 from Globals import qVersionTuple, recentNameBreakpointFiles, \
20 recentNameBreakpointConditions
21
22 import Preferences
23
24
25 class BreakPointViewer(QTreeView):
26 """
27 Class implementing the Breakpoint viewer widget.
28
29 Breakpoints will be shown with all their details. They can be modified
30 through the context menu of this widget.
31
32 @signal sourceFile(str, int) emitted to show the source of a breakpoint
33 """
34 sourceFile = pyqtSignal(str, int)
35
36 def __init__(self, parent=None):
37 """
38 Constructor
39
40 @param parent the parent (QWidget)
41 """
42 super(BreakPointViewer, self).__init__(parent)
43 self.setObjectName("BreakPointViewer")
44
45 self.__model = None
46
47 self.setItemsExpandable(False)
48 self.setRootIsDecorated(False)
49 self.setAlternatingRowColors(True)
50 self.setSelectionMode(QAbstractItemView.ExtendedSelection)
51 self.setSelectionBehavior(QAbstractItemView.SelectRows)
52
53 self.setWindowTitle(self.tr("Breakpoints"))
54
55 self.setContextMenuPolicy(Qt.CustomContextMenu)
56 self.customContextMenuRequested.connect(self.__showContextMenu)
57 self.doubleClicked.connect(self.__doubleClicked)
58
59 self.__createPopupMenus()
60
61 self.condHistory = []
62 self.fnHistory = []
63 self.fnHistory.append('')
64
65 self.__loadRecent()
66
67 def setModel(self, model):
68 """
69 Public slot to set the breakpoint model.
70
71 @param model reference to the breakpoint model (BreakPointModel)
72 """
73 self.__model = model
74
75 self.sortingModel = QSortFilterProxyModel()
76 self.sortingModel.setDynamicSortFilter(True)
77 self.sortingModel.setSourceModel(self.__model)
78 super(BreakPointViewer, self).setModel(self.sortingModel)
79
80 header = self.header()
81 header.setSortIndicator(0, Qt.AscendingOrder)
82 header.setSortIndicatorShown(True)
83 if qVersionTuple() >= (5, 0, 0):
84 header.setSectionsClickable(True)
85 else:
86 header.setClickable(True)
87
88 self.setSortingEnabled(True)
89
90 self.__layoutDisplay()
91
92 def __layoutDisplay(self):
93 """
94 Private slot to perform a layout operation.
95 """
96 self.__resizeColumns()
97 self.__resort()
98
99 def __resizeColumns(self):
100 """
101 Private slot to resize the view when items get added, edited or
102 deleted.
103 """
104 self.header().resizeSections(QHeaderView.ResizeToContents)
105 self.header().setStretchLastSection(True)
106
107 def __resort(self):
108 """
109 Private slot to resort the tree.
110 """
111 self.model().sort(self.header().sortIndicatorSection(),
112 self.header().sortIndicatorOrder())
113
114 def __toSourceIndex(self, index):
115 """
116 Private slot to convert an index to a source index.
117
118 @param index index to be converted (QModelIndex)
119 @return mapped index (QModelIndex)
120 """
121 return self.sortingModel.mapToSource(index)
122
123 def __fromSourceIndex(self, sindex):
124 """
125 Private slot to convert a source index to an index.
126
127 @param sindex source index to be converted (QModelIndex)
128 @return mapped index (QModelIndex)
129 """
130 return self.sortingModel.mapFromSource(sindex)
131
132 def __setRowSelected(self, index, selected=True):
133 """
134 Private slot to select a complete row.
135
136 @param index index determining the row to be selected (QModelIndex)
137 @param selected flag indicating the action (bool)
138 """
139 if not index.isValid():
140 return
141
142 if selected:
143 flags = QItemSelectionModel.SelectionFlags(
144 QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
145 else:
146 flags = QItemSelectionModel.SelectionFlags(
147 QItemSelectionModel.Deselect | QItemSelectionModel.Rows)
148 self.selectionModel().select(index, flags)
149
150 def __createPopupMenus(self):
151 """
152 Private method to generate the popup menus.
153 """
154 self.menu = QMenu()
155 self.menu.addAction(self.tr("Add"), self.__addBreak)
156 self.menu.addAction(self.tr("Edit..."), self.__editBreak)
157 self.menu.addSeparator()
158 self.menu.addAction(self.tr("Enable"), self.__enableBreak)
159 self.menu.addAction(self.tr("Enable all"), self.__enableAllBreaks)
160 self.menu.addSeparator()
161 self.menu.addAction(self.tr("Disable"), self.__disableBreak)
162 self.menu.addAction(self.tr("Disable all"),
163 self.__disableAllBreaks)
164 self.menu.addSeparator()
165 self.menu.addAction(self.tr("Delete"), self.__deleteBreak)
166 self.menu.addAction(self.tr("Delete all"), self.__deleteAllBreaks)
167 self.menu.addSeparator()
168 self.menu.addAction(self.tr("Goto"), self.__showSource)
169 self.menu.addSeparator()
170 self.menu.addAction(self.tr("Configure..."), self.__configure)
171
172 self.backMenuActions = {}
173 self.backMenu = QMenu()
174 self.backMenu.addAction(self.tr("Add"), self.__addBreak)
175 self.backMenuActions["EnableAll"] = \
176 self.backMenu.addAction(self.tr("Enable all"),
177 self.__enableAllBreaks)
178 self.backMenuActions["DisableAll"] = \
179 self.backMenu.addAction(self.tr("Disable all"),
180 self.__disableAllBreaks)
181 self.backMenuActions["DeleteAll"] = \
182 self.backMenu.addAction(self.tr("Delete all"),
183 self.__deleteAllBreaks)
184 self.backMenu.aboutToShow.connect(self.__showBackMenu)
185 self.backMenu.addSeparator()
186 self.backMenu.addAction(self.tr("Configure..."), self.__configure)
187
188 self.multiMenu = QMenu()
189 self.multiMenu.addAction(self.tr("Add"), self.__addBreak)
190 self.multiMenu.addSeparator()
191 self.multiMenu.addAction(self.tr("Enable selected"),
192 self.__enableSelectedBreaks)
193 self.multiMenu.addAction(self.tr("Enable all"),
194 self.__enableAllBreaks)
195 self.multiMenu.addSeparator()
196 self.multiMenu.addAction(self.tr("Disable selected"),
197 self.__disableSelectedBreaks)
198 self.multiMenu.addAction(self.tr("Disable all"),
199 self.__disableAllBreaks)
200 self.multiMenu.addSeparator()
201 self.multiMenu.addAction(self.tr("Delete selected"),
202 self.__deleteSelectedBreaks)
203 self.multiMenu.addAction(self.tr("Delete all"),
204 self.__deleteAllBreaks)
205 self.multiMenu.addSeparator()
206 self.multiMenu.addAction(self.tr("Configure..."), self.__configure)
207
208 def __showContextMenu(self, coord):
209 """
210 Private slot to show the context menu.
211
212 @param coord the position of the mouse pointer (QPoint)
213 """
214 cnt = self.__getSelectedItemsCount()
215 if cnt <= 1:
216 index = self.indexAt(coord)
217 if index.isValid():
218 cnt = 1
219 self.__setRowSelected(index)
220 coord = self.mapToGlobal(coord)
221 if cnt > 1:
222 self.multiMenu.popup(coord)
223 elif cnt == 1:
224 self.menu.popup(coord)
225 else:
226 self.backMenu.popup(coord)
227
228 def __clearSelection(self):
229 """
230 Private slot to clear the selection.
231 """
232 for index in self.selectedIndexes():
233 self.__setRowSelected(index, False)
234
235 def __addBreak(self):
236 """
237 Private slot to handle the add breakpoint context menu entry.
238 """
239 from .EditBreakpointDialog import EditBreakpointDialog
240
241 dlg = EditBreakpointDialog((self.fnHistory[0], None), None,
242 self.condHistory, self, modal=1,
243 addMode=1, filenameHistory=self.fnHistory)
244 if dlg.exec_() == QDialog.Accepted:
245 fn, line, cond, temp, enabled, count = dlg.getAddData()
246 if fn is not None:
247 if fn in self.fnHistory:
248 self.fnHistory.remove(fn)
249 self.fnHistory.insert(0, fn)
250
251 if cond:
252 if cond in self.condHistory:
253 self.condHistory.remove(cond)
254 self.condHistory.insert(0, cond)
255
256 self.__saveRecent()
257
258 self.__model.addBreakPoint(fn, line, (cond, temp, enabled, count))
259 self.__resizeColumns()
260 self.__resort()
261
262 def __doubleClicked(self, index):
263 """
264 Private slot to handle the double clicked signal.
265
266 @param index index of the entry that was double clicked (QModelIndex)
267 """
268 if index.isValid():
269 self.__editBreakpoint(index)
270
271 def __editBreak(self):
272 """
273 Private slot to handle the edit breakpoint context menu entry.
274 """
275 index = self.currentIndex()
276 if index.isValid():
277 self.__editBreakpoint(index)
278
279 def __editBreakpoint(self, index):
280 """
281 Private slot to edit a breakpoint.
282
283 @param index index of breakpoint to be edited (QModelIndex)
284 """
285 sindex = self.__toSourceIndex(index)
286 if sindex.isValid():
287 bp = self.__model.getBreakPointByIndex(sindex)
288 if not bp:
289 return
290
291 fn, line, cond, temp, enabled, count = bp[:6]
292
293 from .EditBreakpointDialog import EditBreakpointDialog
294 dlg = EditBreakpointDialog(
295 (fn, line), (cond, temp, enabled, count),
296 self.condHistory, self, modal=True)
297 if dlg.exec_() == QDialog.Accepted:
298 cond, temp, enabled, count = dlg.getData()
299 if cond:
300 if cond in self.condHistory:
301 self.condHistory.remove(cond)
302 self.condHistory.insert(0, cond)
303
304 self.__saveRecent()
305
306 self.__model.setBreakPointByIndex(
307 sindex, fn, line, (cond, temp, enabled, count))
308 self.__resizeColumns()
309 self.__resort()
310
311 def __setBpEnabled(self, index, enabled):
312 """
313 Private method to set the enabled status of a breakpoint.
314
315 @param index index of breakpoint to be enabled/disabled (QModelIndex)
316 @param enabled flag indicating the enabled status to be set (boolean)
317 """
318 sindex = self.__toSourceIndex(index)
319 if sindex.isValid():
320 self.__model.setBreakPointEnabledByIndex(sindex, enabled)
321
322 def __enableBreak(self):
323 """
324 Private slot to handle the enable breakpoint context menu entry.
325 """
326 index = self.currentIndex()
327 self.__setBpEnabled(index, True)
328 self.__resizeColumns()
329 self.__resort()
330
331 def __enableAllBreaks(self):
332 """
333 Private slot to handle the enable all breakpoints context menu entry.
334 """
335 index = self.model().index(0, 0)
336 while index.isValid():
337 self.__setBpEnabled(index, True)
338 index = self.indexBelow(index)
339 self.__resizeColumns()
340 self.__resort()
341
342 def __enableSelectedBreaks(self):
343 """
344 Private slot to handle the enable selected breakpoints context menu
345 entry.
346 """
347 for index in self.selectedIndexes():
348 if index.column() == 0:
349 self.__setBpEnabled(index, True)
350 self.__resizeColumns()
351 self.__resort()
352
353 def __disableBreak(self):
354 """
355 Private slot to handle the disable breakpoint context menu entry.
356 """
357 index = self.currentIndex()
358 self.__setBpEnabled(index, False)
359 self.__resizeColumns()
360 self.__resort()
361
362 def __disableAllBreaks(self):
363 """
364 Private slot to handle the disable all breakpoints context menu entry.
365 """
366 index = self.model().index(0, 0)
367 while index.isValid():
368 self.__setBpEnabled(index, False)
369 index = self.indexBelow(index)
370 self.__resizeColumns()
371 self.__resort()
372
373 def __disableSelectedBreaks(self):
374 """
375 Private slot to handle the disable selected breakpoints context menu
376 entry.
377 """
378 for index in self.selectedIndexes():
379 if index.column() == 0:
380 self.__setBpEnabled(index, False)
381 self.__resizeColumns()
382 self.__resort()
383
384 def __deleteBreak(self):
385 """
386 Private slot to handle the delete breakpoint context menu entry.
387 """
388 index = self.currentIndex()
389 sindex = self.__toSourceIndex(index)
390 if sindex.isValid():
391 self.__model.deleteBreakPointByIndex(sindex)
392
393 def __deleteAllBreaks(self):
394 """
395 Private slot to handle the delete all breakpoints context menu entry.
396 """
397 self.__model.deleteAll()
398
399 def __deleteSelectedBreaks(self):
400 """
401 Private slot to handle the delete selected breakpoints context menu
402 entry.
403 """
404 idxList = []
405 for index in self.selectedIndexes():
406 sindex = self.__toSourceIndex(index)
407 if sindex.isValid() and index.column() == 0:
408 idxList.append(sindex)
409 self.__model.deleteBreakPoints(idxList)
410
411 def __showSource(self):
412 """
413 Private slot to handle the goto context menu entry.
414 """
415 index = self.currentIndex()
416 sindex = self.__toSourceIndex(index)
417 bp = self.__model.getBreakPointByIndex(sindex)
418 if not bp:
419 return
420
421 fn, line = bp[:2]
422 self.sourceFile.emit(fn, line)
423
424 def highlightBreakpoint(self, fn, lineno):
425 """
426 Public slot to handle the clientLine signal.
427
428 @param fn filename of the breakpoint (string)
429 @param lineno line number of the breakpoint (integer)
430 """
431 sindex = self.__model.getBreakPointIndex(fn, lineno)
432 if sindex.isValid():
433 return
434
435 index = self.__fromSourceIndex(sindex)
436 if index.isValid():
437 self.__clearSelection()
438 self.__setRowSelected(index, True)
439
440 def handleResetUI(self):
441 """
442 Public slot to reset the breakpoint viewer.
443 """
444 self.__clearSelection()
445
446 def __showBackMenu(self):
447 """
448 Private slot to handle the aboutToShow signal of the background menu.
449 """
450 if self.model().rowCount() == 0:
451 self.backMenuActions["EnableAll"].setEnabled(False)
452 self.backMenuActions["DisableAll"].setEnabled(False)
453 self.backMenuActions["DeleteAll"].setEnabled(False)
454 else:
455 self.backMenuActions["EnableAll"].setEnabled(True)
456 self.backMenuActions["DisableAll"].setEnabled(True)
457 self.backMenuActions["DeleteAll"].setEnabled(True)
458
459 def __getSelectedItemsCount(self):
460 """
461 Private method to get the count of items selected.
462
463 @return count of items selected (integer)
464 """
465 count = len(self.selectedIndexes()) // (self.__model.columnCount() - 1)
466 # column count is 1 greater than selectable
467 return count
468
469 def __configure(self):
470 """
471 Private method to open the configuration dialog.
472 """
473 e5App().getObject("UserInterface").showPreferences(
474 "debuggerGeneralPage")
475
476 def __loadRecent(self):
477 """
478 Private method to load the recently used file names.
479 """
480 Preferences.Prefs.rsettings.sync()
481
482 # load recently used file names
483 self.fnHistory = []
484 self.fnHistory.append('')
485 rs = Preferences.Prefs.rsettings.value(recentNameBreakpointFiles)
486 if rs is not None:
487 recent = [f
488 for f in Preferences.toList(rs)
489 if QFileInfo(f).exists()]
490 self.fnHistory.extend(
491 recent[:Preferences.getDebugger("RecentNumber")])
492
493 # load recently entered condition expressions
494 self.condHistory = []
495 rs = Preferences.Prefs.rsettings.value(recentNameBreakpointConditions)
496 if rs is not None:
497 self.condHistory = \
498 Preferences.toList(rs)[
499 :Preferences.getDebugger("RecentNumber")]
500
501 def __saveRecent(self):
502 """
503 Private method to save the list of recently used file names.
504 """
505 recent = [f for f in self.fnHistory if f]
506 Preferences.Prefs.rsettings.setValue(recentNameBreakpointFiles, recent)
507 Preferences.Prefs.rsettings.setValue(recentNameBreakpointConditions,
508 self.condHistory)
509 Preferences.Prefs.rsettings.sync()

eric ide

mercurial