src/eric7/Debugger/WatchPointViewer.py

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

eric ide

mercurial