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