|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2006 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the Breakpoint model. |
|
8 """ |
|
9 |
|
10 import copy |
|
11 |
|
12 from PyQt6.QtCore import pyqtSignal, Qt, QAbstractItemModel, QModelIndex |
|
13 |
|
14 |
|
15 # TODO: change column numbers to class attributes |
|
16 class BreakPointModel(QAbstractItemModel): |
|
17 """ |
|
18 Class implementing a custom model for breakpoints. |
|
19 |
|
20 @signal dataAboutToBeChanged(QModelIndex, QModelIndex) emitted to indicate |
|
21 a change of the data |
|
22 """ |
|
23 dataAboutToBeChanged = pyqtSignal(QModelIndex, QModelIndex) |
|
24 |
|
25 def __init__(self, project, parent=None): |
|
26 """ |
|
27 Constructor |
|
28 |
|
29 @param project reference to the project object |
|
30 @type Project |
|
31 @param parent reference to the parent widget |
|
32 @type QObject |
|
33 """ |
|
34 super().__init__(parent) |
|
35 |
|
36 self.__project = project |
|
37 |
|
38 self.breakpoints = [] |
|
39 self.header = [ |
|
40 self.tr("Filename"), |
|
41 self.tr("Line"), |
|
42 self.tr('Condition'), |
|
43 self.tr('Temporary'), |
|
44 self.tr('Enabled'), |
|
45 self.tr('Ignore Count'), |
|
46 ] |
|
47 self.alignments = [Qt.AlignmentFlag.AlignLeft, |
|
48 Qt.AlignmentFlag.AlignRight, |
|
49 Qt.AlignmentFlag.AlignLeft, |
|
50 Qt.AlignmentFlag.AlignHCenter, |
|
51 Qt.AlignmentFlag.AlignHCenter, |
|
52 Qt.AlignmentFlag.AlignRight, |
|
53 Qt.AlignmentFlag.AlignHCenter, |
|
54 ] |
|
55 |
|
56 def columnCount(self, parent=None): |
|
57 """ |
|
58 Public method to get the current column count. |
|
59 |
|
60 @param parent reference to parent index (Unused) |
|
61 @type QModelIndex |
|
62 @return column count |
|
63 @rtype int |
|
64 """ |
|
65 return len(self.header) |
|
66 |
|
67 def rowCount(self, parent=None): |
|
68 """ |
|
69 Public method to get the current row count. |
|
70 |
|
71 @param parent reference to parent index |
|
72 @type QModelIndex |
|
73 @return row count |
|
74 @rtype int |
|
75 """ |
|
76 # we do not have a tree, parent should always be invalid |
|
77 if parent is None or not parent.isValid(): |
|
78 return len(self.breakpoints) |
|
79 else: |
|
80 return 0 |
|
81 |
|
82 def data(self, index, role=Qt.ItemDataRole.DisplayRole): |
|
83 """ |
|
84 Public method to get the requested data. |
|
85 |
|
86 @param index index of the requested data |
|
87 @type QModelIndex |
|
88 @param role role of the requested data |
|
89 @type Qt.ItemDataRole |
|
90 @return the requested data |
|
91 @rtype any |
|
92 """ |
|
93 if not index.isValid(): |
|
94 return None |
|
95 |
|
96 if role == Qt.ItemDataRole.DisplayRole: |
|
97 if index.column() == 0: |
|
98 filename = self.breakpoints[index.row()][0] |
|
99 if self.__project.isOpen(): |
|
100 return self.__project.getRelativePath(filename) |
|
101 else: |
|
102 return filename |
|
103 elif index.column() in (1, 2, 5): |
|
104 return self.breakpoints[index.row()][index.column()] |
|
105 |
|
106 if ( |
|
107 role == Qt.ItemDataRole.CheckStateRole and |
|
108 index.column() in (3, 4) |
|
109 ): |
|
110 if self.breakpoints[index.row()][index.column()]: |
|
111 return Qt.CheckState.Checked |
|
112 else: |
|
113 return Qt.CheckState.Unchecked |
|
114 |
|
115 if ( |
|
116 role == Qt.ItemDataRole.ToolTipRole and |
|
117 index.column() in (0, 2) |
|
118 ): |
|
119 return self.breakpoints[index.row()][index.column()] |
|
120 |
|
121 if ( |
|
122 role == Qt.ItemDataRole.TextAlignmentRole and |
|
123 index.column() < len(self.alignments) |
|
124 ): |
|
125 return self.alignments[index.column()].value |
|
126 |
|
127 return None |
|
128 |
|
129 def setData(self, index, value, role=Qt.ItemDataRole.EditRole): |
|
130 """ |
|
131 Public method to change data in the model. |
|
132 |
|
133 @param index index of the changed data |
|
134 @type QModelIndex |
|
135 @param value value of the changed data |
|
136 @type any |
|
137 @param role role of the changed data |
|
138 @type Qt.ItemDataRole |
|
139 @return flag indicating success |
|
140 @rtype bool |
|
141 """ |
|
142 if (not index.isValid() or |
|
143 index.column() >= len(self.header) or |
|
144 index.row() >= len(self.breakpoints)): |
|
145 return False |
|
146 |
|
147 self.dataAboutToBeChanged.emit(index, index) |
|
148 self.breakpoints[index.row()][index.column()] = value |
|
149 self.dataChanged.emit(index, index) |
|
150 return True |
|
151 |
|
152 def flags(self, index): |
|
153 """ |
|
154 Public method to get item flags. |
|
155 |
|
156 @param index index of the requested flags |
|
157 @type QModelIndex |
|
158 @return item flags for the given index |
|
159 @rtype Qt.ItemFlags |
|
160 """ |
|
161 if not index.isValid(): |
|
162 return Qt.ItemFlag.ItemIsEnabled |
|
163 |
|
164 return Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable |
|
165 |
|
166 def headerData(self, section, orientation, |
|
167 role=Qt.ItemDataRole.DisplayRole): |
|
168 """ |
|
169 Public method to get header data. |
|
170 |
|
171 @param section section number of the requested header data |
|
172 @type int |
|
173 @param orientation orientation of the header |
|
174 @type Qt.Orientation |
|
175 @param role role of the requested data |
|
176 @type Qt.ItemDataRole |
|
177 @return header data |
|
178 @rtype str |
|
179 """ |
|
180 if ( |
|
181 orientation == Qt.Orientation.Horizontal and |
|
182 role == Qt.ItemDataRole.DisplayRole |
|
183 ): |
|
184 if section >= len(self.header): |
|
185 return "" |
|
186 else: |
|
187 return self.header[section] |
|
188 |
|
189 return None |
|
190 |
|
191 def index(self, row, column, parent=None): |
|
192 """ |
|
193 Public method to create an index. |
|
194 |
|
195 @param row row number for the index |
|
196 @type int |
|
197 @param column column number for the index |
|
198 @type int |
|
199 @param parent index of the parent item |
|
200 @type QModelIndex |
|
201 @return requested index |
|
202 @rtype QModelIndex |
|
203 """ |
|
204 if ((parent and parent.isValid()) or |
|
205 row < 0 or row >= len(self.breakpoints) or |
|
206 column < 0 or column >= len(self.header)): |
|
207 return QModelIndex() |
|
208 |
|
209 return self.createIndex(row, column, self.breakpoints[row]) |
|
210 |
|
211 def parent(self, index): |
|
212 """ |
|
213 Public method to get the parent index. |
|
214 |
|
215 @param index index of item to get parent |
|
216 @type QModelIndex |
|
217 @return index of parent |
|
218 @rtype QModelIndex |
|
219 """ |
|
220 return QModelIndex() |
|
221 |
|
222 def hasChildren(self, parent=None): |
|
223 """ |
|
224 Public method to check for the presence of child items. |
|
225 |
|
226 @param parent index of parent item |
|
227 @type QModelIndex |
|
228 @return flag indicating the presence of child items |
|
229 @rtype bool |
|
230 """ |
|
231 if parent is None or not parent.isValid(): |
|
232 return len(self.breakpoints) > 0 |
|
233 else: |
|
234 return False |
|
235 |
|
236 ########################################################################### |
|
237 |
|
238 def addBreakPoint(self, fn, line, properties): |
|
239 """ |
|
240 Public method to add a new breakpoint to the list. |
|
241 |
|
242 @param fn filename of the breakpoint |
|
243 @type str |
|
244 @param line line number of the breakpoint |
|
245 @type int |
|
246 @param properties properties of the breakpoint |
|
247 (tuple of condition, temporary flag, enabled flag, ignore count) |
|
248 @type tuple of (str, bool, bool, int) |
|
249 """ |
|
250 bp = [fn, line] + list(properties) |
|
251 cnt = len(self.breakpoints) |
|
252 self.beginInsertRows(QModelIndex(), cnt, cnt) |
|
253 self.breakpoints.append(bp) |
|
254 self.endInsertRows() |
|
255 |
|
256 def addBreakPoints(self, breakpoints): |
|
257 """ |
|
258 Public method to add multiple breakpoints to the list. |
|
259 |
|
260 @param breakpoints list of breakpoints with file name, line number, |
|
261 condition, temporary flag, enabled flag and ignore count each |
|
262 @type list of (str, int, str, bool, bool, int) |
|
263 """ |
|
264 cnt = len(self.breakpoints) |
|
265 self.beginInsertRows(QModelIndex(), cnt, cnt + len(breakpoints) - 1) |
|
266 self.breakpoints += breakpoints |
|
267 self.endInsertRows() |
|
268 |
|
269 def setBreakPointByIndex(self, index, fn, line, properties): |
|
270 """ |
|
271 Public method to set the values of a breakpoint given by index. |
|
272 |
|
273 @param index index of the breakpoint |
|
274 @type QModelIndex |
|
275 @param fn filename of the breakpoint |
|
276 @type str |
|
277 @param line line number of the breakpoint |
|
278 @type int |
|
279 @param properties properties of the breakpoint |
|
280 (tuple of condition, temporary flag, enabled flag, ignore count) |
|
281 @type tuple of (str, bool, bool, int) |
|
282 """ |
|
283 if index.isValid(): |
|
284 row = index.row() |
|
285 index1 = self.createIndex(row, 0, self.breakpoints[row]) |
|
286 index2 = self.createIndex( |
|
287 row, len(self.breakpoints[row]) - 1, self.breakpoints[row]) |
|
288 self.dataAboutToBeChanged.emit(index1, index2) |
|
289 self.breakpoints[row] = [fn, line] + list(properties) |
|
290 self.dataChanged.emit(index1, index2) |
|
291 |
|
292 def setBreakPointEnabledByIndex(self, index, enabled): |
|
293 """ |
|
294 Public method to set the enabled state of a breakpoint given by index. |
|
295 |
|
296 @param index index of the breakpoint |
|
297 @type QModelIndex |
|
298 @param enabled flag giving the enabled state |
|
299 @type bool |
|
300 """ |
|
301 if index.isValid(): |
|
302 row = index.row() |
|
303 col = 4 |
|
304 index1 = self.createIndex(row, col, self.breakpoints[row]) |
|
305 self.dataAboutToBeChanged.emit(index1, index1) |
|
306 self.breakpoints[row][col] = enabled |
|
307 self.dataChanged.emit(index1, index1) |
|
308 |
|
309 def deleteBreakPointByIndex(self, index): |
|
310 """ |
|
311 Public method to set the values of a breakpoint given by index. |
|
312 |
|
313 @param index index of the breakpoint |
|
314 @type QModelIndex |
|
315 """ |
|
316 if index.isValid(): |
|
317 row = index.row() |
|
318 self.beginRemoveRows(QModelIndex(), row, row) |
|
319 del self.breakpoints[row] |
|
320 self.endRemoveRows() |
|
321 |
|
322 def deleteBreakPoints(self, idxList): |
|
323 """ |
|
324 Public method to delete a list of breakpoints given by their indexes. |
|
325 |
|
326 @param idxList list of breakpoint indexes |
|
327 @type list of QModelIndex |
|
328 """ |
|
329 rows = [] |
|
330 for index in idxList: |
|
331 if index.isValid(): |
|
332 rows.append(index.row()) |
|
333 rows.sort(reverse=True) |
|
334 for row in rows: |
|
335 if row < len(self.breakpoints): |
|
336 self.beginRemoveRows(QModelIndex(), row, row) |
|
337 del self.breakpoints[row] |
|
338 self.endRemoveRows() |
|
339 |
|
340 def deleteAll(self): |
|
341 """ |
|
342 Public method to delete all breakpoints. |
|
343 """ |
|
344 if self.breakpoints: |
|
345 self.beginRemoveRows(QModelIndex(), 0, len(self.breakpoints) - 1) |
|
346 self.breakpoints = [] |
|
347 self.endRemoveRows() |
|
348 |
|
349 def getBreakPointByIndex(self, index): |
|
350 """ |
|
351 Public method to get the values of a breakpoint given by index. |
|
352 |
|
353 @param index index of the breakpoint |
|
354 @type QModelIndex |
|
355 @return breakpoint (list of six values (filename, line number, |
|
356 condition, temporary flag, enabled flag, ignore count)) |
|
357 @rtype list of (str, int, str, bool, bool, int) |
|
358 """ |
|
359 if index.isValid(): |
|
360 return self.breakpoints[index.row()][:] # return a copy |
|
361 else: |
|
362 return [] |
|
363 |
|
364 def getAllBreakpoints(self): |
|
365 """ |
|
366 Public method to get a copy of the breakpoints. |
|
367 |
|
368 @return list of breakpoints |
|
369 @rtype list of list of [str, int, str, bool, bool, int] |
|
370 """ |
|
371 return copy.deepcopy(self.breakpoints) |
|
372 |
|
373 def getBreakPointIndex(self, fn, lineno): |
|
374 """ |
|
375 Public method to get the index of a breakpoint given by filename and |
|
376 line number. |
|
377 |
|
378 @param fn filename of the breakpoint |
|
379 @type str |
|
380 @param lineno line number of the breakpoint |
|
381 @type int |
|
382 @return index |
|
383 @rtype QModelIndex |
|
384 """ |
|
385 for row in range(len(self.breakpoints)): |
|
386 bp = self.breakpoints[row] |
|
387 if bp[0] == fn and bp[1] == lineno: |
|
388 return self.createIndex(row, 0, self.breakpoints[row]) |
|
389 |
|
390 return QModelIndex() |
|
391 |
|
392 def isBreakPointTemporaryByIndex(self, index): |
|
393 """ |
|
394 Public method to test, if a breakpoint given by its index is temporary. |
|
395 |
|
396 @param index index of the breakpoint to test |
|
397 @type QModelIndex |
|
398 @return flag indicating a temporary breakpoint |
|
399 @rtype bool |
|
400 """ |
|
401 if index.isValid(): |
|
402 return self.breakpoints[index.row()][3] |
|
403 else: |
|
404 return False |