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) |
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() |