eric6/Debugger/WatchPointViewer.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 watch expression viewer widget.
8 """
9
10 from __future__ import unicode_literals
11
12 from PyQt5.QtCore import Qt, QModelIndex, QItemSelectionModel, \
13 QSortFilterProxyModel
14 from PyQt5.QtWidgets import QTreeView, QAbstractItemView, QMenu, QHeaderView, \
15 QDialog
16
17 from E5Gui.E5Application import e5App
18 from E5Gui import E5MessageBox
19
20 import Utilities
21 from Globals import qVersionTuple
22
23
24 class WatchPointViewer(QTreeView):
25 """
26 Class implementing the watch expression viewer widget.
27
28 Watch expressions will be shown with all their details. They can be
29 modified through the context menu of this widget.
30 """
31 def __init__(self, parent=None):
32 """
33 Constructor
34
35 @param parent the parent (QWidget)
36 """
37 super(WatchPointViewer, self).__init__(parent)
38 self.setObjectName("WatchExpressionViewer")
39
40 self.__model = None
41
42 self.setItemsExpandable(False)
43 self.setRootIsDecorated(False)
44 self.setAlternatingRowColors(True)
45 self.setSelectionMode(QAbstractItemView.ExtendedSelection)
46 self.setSelectionBehavior(QAbstractItemView.SelectRows)
47
48 self.setWindowTitle(self.tr("Watchpoints"))
49
50 self.setContextMenuPolicy(Qt.CustomContextMenu)
51 self.customContextMenuRequested.connect(self.__showContextMenu)
52 self.doubleClicked.connect(self.__doubleClicked)
53
54 self.__createPopupMenus()
55
56 def setModel(self, model):
57 """
58 Public slot to set the watch expression model.
59
60 @param model reference to the watch expression model (WatchPointModel)
61 """
62 self.__model = model
63
64 self.sortingModel = QSortFilterProxyModel()
65 self.sortingModel.setDynamicSortFilter(True)
66 self.sortingModel.setSourceModel(self.__model)
67 super(WatchPointViewer, self).setModel(self.sortingModel)
68
69 header = self.header()
70 header.setSortIndicator(0, Qt.AscendingOrder)
71 header.setSortIndicatorShown(True)
72 if qVersionTuple() >= (5, 0, 0):
73 header.setSectionsClickable(True)
74 else:
75 header.setClickable(True)
76
77 self.setSortingEnabled(True)
78
79 self.__layoutDisplay()
80
81 def __layoutDisplay(self):
82 """
83 Private slot to perform a layout operation.
84 """
85 self.__resizeColumns()
86 self.__resort()
87
88 def __resizeColumns(self):
89 """
90 Private slot to resize the view when items get added, edited or
91 deleted.
92 """
93 self.header().resizeSections(QHeaderView.ResizeToContents)
94 self.header().setStretchLastSection(True)
95
96 def __resort(self):
97 """
98 Private slot to resort the tree.
99 """
100 self.model().sort(self.header().sortIndicatorSection(),
101 self.header().sortIndicatorOrder())
102
103 def __toSourceIndex(self, index):
104 """
105 Private slot to convert an index to a source index.
106
107 @param index index to be converted (QModelIndex)
108 @return mapped index (QModelIndex)
109 """
110 return self.sortingModel.mapToSource(index)
111
112 def __fromSourceIndex(self, sindex):
113 """
114 Private slot to convert a source index to an index.
115
116 @param sindex source index to be converted (QModelIndex)
117 @return mapped index (QModelIndex)
118 """
119 return self.sortingModel.mapFromSource(sindex)
120
121 def __setRowSelected(self, index, selected=True):
122 """
123 Private slot to select a complete row.
124
125 @param index index determining the row to be selected (QModelIndex)
126 @param selected flag indicating the action (bool)
127 """
128 if not index.isValid():
129 return
130
131 if selected:
132 flags = QItemSelectionModel.SelectionFlags(
133 QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
134 else:
135 flags = QItemSelectionModel.SelectionFlags(
136 QItemSelectionModel.Deselect | QItemSelectionModel.Rows)
137 self.selectionModel().select(index, flags)
138
139 def __createPopupMenus(self):
140 """
141 Private method to generate the popup menus.
142 """
143 self.menu = QMenu()
144 self.menu.addAction(self.tr("Add"), self.__addWatchPoint)
145 self.menu.addAction(self.tr("Edit..."), self.__editWatchPoint)
146 self.menu.addSeparator()
147 self.menu.addAction(self.tr("Enable"), self.__enableWatchPoint)
148 self.menu.addAction(self.tr("Enable all"),
149 self.__enableAllWatchPoints)
150 self.menu.addSeparator()
151 self.menu.addAction(self.tr("Disable"), self.__disableWatchPoint)
152 self.menu.addAction(self.tr("Disable all"),
153 self.__disableAllWatchPoints)
154 self.menu.addSeparator()
155 self.menu.addAction(self.tr("Delete"), self.__deleteWatchPoint)
156 self.menu.addAction(self.tr("Delete all"),
157 self.__deleteAllWatchPoints)
158 self.menu.addSeparator()
159 self.menu.addAction(self.tr("Configure..."), self.__configure)
160
161 self.backMenuActions = {}
162 self.backMenu = QMenu()
163 self.backMenu.addAction(self.tr("Add"), self.__addWatchPoint)
164 self.backMenuActions["EnableAll"] = \
165 self.backMenu.addAction(self.tr("Enable all"),
166 self.__enableAllWatchPoints)
167 self.backMenuActions["DisableAll"] = \
168 self.backMenu.addAction(self.tr("Disable all"),
169 self.__disableAllWatchPoints)
170 self.backMenuActions["DeleteAll"] = \
171 self.backMenu.addAction(self.tr("Delete all"),
172 self.__deleteAllWatchPoints)
173 self.backMenu.addSeparator()
174 self.backMenu.addAction(self.tr("Configure..."), self.__configure)
175 self.backMenu.aboutToShow.connect(self.__showBackMenu)
176
177 self.multiMenu = QMenu()
178 self.multiMenu.addAction(self.tr("Add"), self.__addWatchPoint)
179 self.multiMenu.addSeparator()
180 self.multiMenu.addAction(self.tr("Enable selected"),
181 self.__enableSelectedWatchPoints)
182 self.multiMenu.addAction(self.tr("Enable all"),
183 self.__enableAllWatchPoints)
184 self.multiMenu.addSeparator()
185 self.multiMenu.addAction(self.tr("Disable selected"),
186 self.__disableSelectedWatchPoints)
187 self.multiMenu.addAction(self.tr("Disable all"),
188 self.__disableAllWatchPoints)
189 self.multiMenu.addSeparator()
190 self.multiMenu.addAction(self.tr("Delete selected"),
191 self.__deleteSelectedWatchPoints)
192 self.multiMenu.addAction(self.tr("Delete all"),
193 self.__deleteAllWatchPoints)
194 self.multiMenu.addSeparator()
195 self.multiMenu.addAction(self.tr("Configure..."), self.__configure)
196
197 def __showContextMenu(self, coord):
198 """
199 Private slot to show the context menu.
200
201 @param coord the position of the mouse pointer (QPoint)
202 """
203 cnt = self.__getSelectedItemsCount()
204 if cnt <= 1:
205 index = self.indexAt(coord)
206 if index.isValid():
207 cnt = 1
208 self.__setRowSelected(index)
209 coord = self.mapToGlobal(coord)
210 if cnt > 1:
211 self.multiMenu.popup(coord)
212 elif cnt == 1:
213 self.menu.popup(coord)
214 else:
215 self.backMenu.popup(coord)
216
217 def __clearSelection(self):
218 """
219 Private slot to clear the selection.
220 """
221 for index in self.selectedIndexes():
222 self.__setRowSelected(index, False)
223
224 def __findDuplicates(self, cond, special, showMessage=False,
225 index=None):
226 """
227 Private method to check, if an entry already exists.
228
229 @param cond condition to check (string)
230 @param special special condition to check (string)
231 @param showMessage flag indicating a message should be shown,
232 if a duplicate entry is found (boolean)
233 @param index index that should not be considered duplicate
234 (QModelIndex)
235 @return flag indicating a duplicate entry (boolean)
236 """
237 if index is None:
238 index = QModelIndex()
239 idx = self.__model.getWatchPointIndex(cond, special)
240 duplicate = idx.isValid() and \
241 idx.internalPointer() != index.internalPointer()
242 if showMessage and duplicate:
243 if not special:
244 msg = self.tr("""<p>A watch expression '<b>{0}</b>'"""
245 """ already exists.</p>""")\
246 .format(Utilities.html_encode(cond))
247 else:
248 msg = self.tr(
249 """<p>A watch expression '<b>{0}</b>'"""
250 """ for the variable <b>{1}</b> already exists.</p>""")\
251 .format(special, Utilities.html_encode(cond))
252 E5MessageBox.warning(
253 self,
254 self.tr("Watch expression already exists"),
255 msg)
256
257 return duplicate
258
259 def __addWatchPoint(self):
260 """
261 Private slot to handle the add watch expression context menu entry.
262 """
263 from .EditWatchpointDialog import EditWatchpointDialog
264 dlg = EditWatchpointDialog(("", False, True, 0, ""), self)
265 if dlg.exec_() == QDialog.Accepted:
266 cond, temp, enabled, ignorecount, special = dlg.getData()
267 if not self.__findDuplicates(cond, special, True):
268 self.__model.addWatchPoint(cond, special,
269 (temp, enabled, ignorecount))
270 self.__resizeColumns()
271 self.__resort()
272
273 def __doubleClicked(self, index):
274 """
275 Private slot to handle the double clicked signal.
276
277 @param index index of the entry that was double clicked (QModelIndex)
278 """
279 if index.isValid():
280 self.__doEditWatchPoint(index)
281
282 def __editWatchPoint(self):
283 """
284 Private slot to handle the edit watch expression context menu entry.
285 """
286 index = self.currentIndex()
287 if index.isValid():
288 self.__doEditWatchPoint(index)
289
290 def __doEditWatchPoint(self, index):
291 """
292 Private slot to edit a watch expression.
293
294 @param index index of watch expression to be edited (QModelIndex)
295 """
296 sindex = self.__toSourceIndex(index)
297 if sindex.isValid():
298 wp = self.__model.getWatchPointByIndex(sindex)
299 if not wp:
300 return
301
302 cond, special, temp, enabled, count = wp[:5]
303
304 from .EditWatchpointDialog import EditWatchpointDialog
305 dlg = EditWatchpointDialog(
306 (cond, temp, enabled, count, special), self)
307 if dlg.exec_() == QDialog.Accepted:
308 cond, temp, enabled, count, special = dlg.getData()
309 if not self.__findDuplicates(cond, special, True, sindex):
310 self.__model.setWatchPointByIndex(
311 sindex, cond, special, (temp, enabled, count))
312 self.__resizeColumns()
313 self.__resort()
314
315 def __setWpEnabled(self, index, enabled):
316 """
317 Private method to set the enabled status of a watch expression.
318
319 @param index index of watch expression to be enabled/disabled
320 (QModelIndex)
321 @param enabled flag indicating the enabled status to be set (boolean)
322 """
323 sindex = self.__toSourceIndex(index)
324 if sindex.isValid():
325 self.__model.setWatchPointEnabledByIndex(sindex, enabled)
326
327 def __enableWatchPoint(self):
328 """
329 Private slot to handle the enable watch expression context menu entry.
330 """
331 index = self.currentIndex()
332 self.__setWpEnabled(index, True)
333 self.__resizeColumns()
334 self.__resort()
335
336 def __enableAllWatchPoints(self):
337 """
338 Private slot to handle the enable all watch expressions context menu
339 entry.
340 """
341 index = self.model().index(0, 0)
342 while index.isValid():
343 self.__setWpEnabled(index, True)
344 index = self.indexBelow(index)
345 self.__resizeColumns()
346 self.__resort()
347
348 def __enableSelectedWatchPoints(self):
349 """
350 Private slot to handle the enable selected watch expressions context
351 menu entry.
352 """
353 for index in self.selectedIndexes():
354 if index.column() == 0:
355 self.__setWpEnabled(index, True)
356 self.__resizeColumns()
357 self.__resort()
358
359 def __disableWatchPoint(self):
360 """
361 Private slot to handle the disable watch expression context menu entry.
362 """
363 index = self.currentIndex()
364 self.__setWpEnabled(index, False)
365 self.__resizeColumns()
366 self.__resort()
367
368 def __disableAllWatchPoints(self):
369 """
370 Private slot to handle the disable all watch expressions context menu
371 entry.
372 """
373 index = self.model().index(0, 0)
374 while index.isValid():
375 self.__setWpEnabled(index, False)
376 index = self.indexBelow(index)
377 self.__resizeColumns()
378 self.__resort()
379
380 def __disableSelectedWatchPoints(self):
381 """
382 Private slot to handle the disable selected watch expressions context
383 menu entry.
384 """
385 for index in self.selectedIndexes():
386 if index.column() == 0:
387 self.__setWpEnabled(index, False)
388 self.__resizeColumns()
389 self.__resort()
390
391 def __deleteWatchPoint(self):
392 """
393 Private slot to handle the delete watch expression context menu entry.
394 """
395 index = self.currentIndex()
396 sindex = self.__toSourceIndex(index)
397 if sindex.isValid():
398 self.__model.deleteWatchPointByIndex(sindex)
399
400 def __deleteAllWatchPoints(self):
401 """
402 Private slot to handle the delete all watch expressions context menu
403 entry.
404 """
405 self.__model.deleteAll()
406
407 def __deleteSelectedWatchPoints(self):
408 """
409 Private slot to handle the delete selected watch expressions context
410 menu entry.
411 """
412 idxList = []
413 for index in self.selectedIndexes():
414 sindex = self.__toSourceIndex(index)
415 if sindex.isValid() and index.column() == 0:
416 idxList.append(sindex)
417 self.__model.deleteWatchPoints(idxList)
418
419 def __showBackMenu(self):
420 """
421 Private slot to handle the aboutToShow signal of the background menu.
422 """
423 if self.model().rowCount() == 0:
424 self.backMenuActions["EnableAll"].setEnabled(False)
425 self.backMenuActions["DisableAll"].setEnabled(False)
426 self.backMenuActions["DeleteAll"].setEnabled(False)
427 else:
428 self.backMenuActions["EnableAll"].setEnabled(True)
429 self.backMenuActions["DisableAll"].setEnabled(True)
430 self.backMenuActions["DeleteAll"].setEnabled(True)
431
432 def __getSelectedItemsCount(self):
433 """
434 Private method to get the count of items selected.
435
436 @return count of items selected (integer)
437 """
438 count = len(self.selectedIndexes()) // (self.__model.columnCount() - 1)
439 # column count is 1 greater than selectable
440 return count
441
442 def __configure(self):
443 """
444 Private method to open the configuration dialog.
445 """
446 e5App().getObject("UserInterface")\
447 .showPreferences("debuggerGeneralPage")

eric ide

mercurial