src/eric7/Debugger/VariablesViewer.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9397
a415cb83dafb
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
10 import ast 10 import ast
11 import re 11 import re
12 import contextlib 12 import contextlib
13 13
14 from PyQt6.QtCore import ( 14 from PyQt6.QtCore import (
15 Qt, QAbstractItemModel, QModelIndex, QCoreApplication, 15 Qt,
16 QSortFilterProxyModel, pyqtSignal 16 QAbstractItemModel,
17 QModelIndex,
18 QCoreApplication,
19 QSortFilterProxyModel,
20 pyqtSignal,
17 ) 21 )
18 from PyQt6.QtGui import QBrush, QFontMetrics 22 from PyQt6.QtGui import QBrush, QFontMetrics
19 from PyQt6.QtWidgets import QTreeView, QAbstractItemView, QToolTip, QMenu 23 from PyQt6.QtWidgets import QTreeView, QAbstractItemView, QToolTip, QMenu
20 24
21 from EricWidgets.EricApplication import ericApp 25 from EricWidgets.EricApplication import ericApp
30 34
31 class VariableItem: 35 class VariableItem:
32 """ 36 """
33 Class implementing the data structure for all variable items. 37 Class implementing the data structure for all variable items.
34 """ 38 """
39
35 # Initialize regular expression for unprintable strings 40 # Initialize regular expression for unprintable strings
36 rx_nonprintable = re.compile(r"""(\\x\d\d)+""") 41 rx_nonprintable = re.compile(r"""(\\x\d\d)+""")
37 42
38 noOfItemsStr = QCoreApplication.translate("VariablesViewer", "{0} items") 43 noOfItemsStr = QCoreApplication.translate("VariablesViewer", "{0} items")
39 unsized = QCoreApplication.translate("VariablesViewer", "unsized") 44 unsized = QCoreApplication.translate("VariablesViewer", "unsized")
40 45
41 def __init__(self, parent, dvar, indicator, dtype, hasChildren, length, 46 def __init__(self, parent, dvar, indicator, dtype, hasChildren, length, dvalue):
42 dvalue):
43 """ 47 """
44 Constructor 48 Constructor
45 49
46 @param parent reference to the parent item 50 @param parent reference to the parent item
47 @type VariableItem 51 @type VariableItem
48 @param dvar variable name 52 @param dvar variable name
49 @type str 53 @type str
50 @param indicator type indicator appended to the name 54 @param indicator type indicator appended to the name
67 # Indicator that there are children 71 # Indicator that there are children
68 self.hasChildren = hasChildren 72 self.hasChildren = hasChildren
69 self.populated = False 73 self.populated = False
70 # Indicator that item was at least once fully populated 74 # Indicator that item was at least once fully populated
71 self.wasPopulated = False 75 self.wasPopulated = False
72 76
73 self.children = [] 77 self.children = []
74 # Flag to prevent endless reloading of current item while waiting on 78 # Flag to prevent endless reloading of current item while waiting on
75 # a response from debugger 79 # a response from debugger
76 self.pendigFetch = False 80 self.pendigFetch = False
77 81
78 # Set of child items, which are displayed the first time or changed 82 # Set of child items, which are displayed the first time or changed
79 self.newItems = set() 83 self.newItems = set()
80 self.changedItems = set() 84 self.changedItems = set()
81 # Name including its ID if it's a dict, set, etc. 85 # Name including its ID if it's a dict, set, etc.
82 self.nameWithId = dvar 86 self.nameWithId = dvar
83 87
84 self.name = '' 88 self.name = ""
85 self.sort = '' 89 self.sort = ""
86 vtype = ConfigVarTypeDispStrings.get(dtype, dtype) 90 vtype = ConfigVarTypeDispStrings.get(dtype, dtype)
87 self.type = QCoreApplication.translate("VariablesViewer", vtype) 91 self.type = QCoreApplication.translate("VariablesViewer", vtype)
88 self.indicator = indicator 92 self.indicator = indicator
89 self.value = dvalue 93 self.value = dvalue
90 self.valueShort = None 94 self.valueShort = None
91 self.tooltip = '' 95 self.tooltip = ""
92 96
93 self.__getName(dvar) 97 self.__getName(dvar)
94 self.__getValue(dtype, dvalue, indicator, length) 98 self.__getValue(dtype, dvalue, indicator, length)
95 99
96 def __getName(self, dvar): 100 def __getName(self, dvar):
97 """ 101 """
98 Private method to extract the variable name. 102 Private method to extract the variable name.
99 103
100 @param dvar name of variable maybe with ID 104 @param dvar name of variable maybe with ID
101 @type str 105 @type str
102 """ 106 """
103 try: 107 try:
104 idx = dvar.index(" (ID:") 108 idx = dvar.index(" (ID:")
106 except AttributeError: 110 except AttributeError:
107 idx = dvar 111 idx = dvar
108 dvar = str(dvar) 112 dvar = str(dvar)
109 except ValueError: 113 except ValueError:
110 pass 114 pass
111 115
112 self.name = dvar 116 self.name = dvar
113 try: 117 try:
114 # Convert numbers to strings with preceding zeros 118 # Convert numbers to strings with preceding zeros
115 asInt = int(dvar) 119 asInt = int(dvar)
116 self.sort = "{0:06}".format(asInt) 120 self.sort = "{0:06}".format(asInt)
117 except ValueError: 121 except ValueError:
118 self.sort = dvar.lower() 122 self.sort = dvar.lower()
119 123
120 def __getValue(self, dtype, dvalue, indicator, length): 124 def __getValue(self, dtype, dvalue, indicator, length):
121 """ 125 """
122 Private method to process the variables value. 126 Private method to process the variables value.
123 127
124 Define and limit value, set tooltip text. If type is known to have 128 Define and limit value, set tooltip text. If type is known to have
125 children, the corresponding flag is set. 129 children, the corresponding flag is set.
126 130
127 @param dtype type string 131 @param dtype type string
128 @type str 132 @type str
129 @param dvalue value of variable encoded as utf-8 133 @param dvalue value of variable encoded as utf-8
130 @type str 134 @type str
131 @param indicator type indicator appended to the name 135 @param indicator type indicator appended to the name
134 numpy.ndarray) 138 numpy.ndarray)
135 @type int or str 139 @type int or str
136 """ 140 """
137 length_code = length 141 length_code = length
138 if isinstance(length, str): 142 if isinstance(length, str):
139 length = int(length.split('x')[0]) 143 length = int(length.split("x")[0])
140 144
141 if indicator and length > -2: 145 if indicator and length > -2:
142 self.childCount = max(0, length) # Update count if array 146 self.childCount = max(0, length) # Update count if array
143 if dtype == 'numpy.ndarray' and length == -1: 147 if dtype == "numpy.ndarray" and length == -1:
144 self.value = VariableItem.unsized 148 self.value = VariableItem.unsized
145 else: 149 else:
146 self.value = VariableItem.noOfItemsStr.format(length_code) 150 self.value = VariableItem.noOfItemsStr.format(length_code)
147 151
148 if dtype != 'str': 152 if dtype != "str":
149 self.valueShort = self.value 153 self.valueShort = self.value
150 self.tooltip = str(self.value)[:256] 154 self.tooltip = str(self.value)[:256]
151 return 155 return
152 156
153 if VariableItem.rx_nonprintable.search(dvalue) is None: 157 if VariableItem.rx_nonprintable.search(dvalue) is None:
154 with contextlib.suppress(Exception): 158 with contextlib.suppress(Exception):
155 dvalue = ast.literal_eval(dvalue) 159 dvalue = ast.literal_eval(dvalue)
156 160
157 dvalue = str(dvalue) 161 dvalue = str(dvalue)
158 self.value = dvalue 162 self.value = dvalue
159 163
160 if len(dvalue) > 2048: # 2 kB 164 if len(dvalue) > 2048: # 2 kB
161 self.tooltip = dvalue[:2048] 165 self.tooltip = dvalue[:2048]
162 dvalue = QCoreApplication.translate( 166 dvalue = QCoreApplication.translate(
163 "VariableItem", "<double click to show value>") 167 "VariableItem", "<double click to show value>"
168 )
164 else: 169 else:
165 self.tooltip = dvalue 170 self.tooltip = dvalue
166 171
167 lines = dvalue[:2048].splitlines() 172 lines = dvalue[:2048].splitlines()
168 if len(lines) > 1: 173 if len(lines) > 1:
169 # only show the first non-empty line; 174 # only show the first non-empty line;
170 # indicate skipped lines by <...> at the 175 # indicate skipped lines by <...> at the
171 # beginning and/or end 176 # beginning and/or end
172 index = 0 177 index = 0
173 while index < len(lines) - 1 and lines[index].strip(' \t') == "": 178 while index < len(lines) - 1 and lines[index].strip(" \t") == "":
174 index += 1 179 index += 1
175 180
176 dvalue = "" 181 dvalue = ""
177 if index > 0: 182 if index > 0:
178 dvalue += "<...>" 183 dvalue += "<...>"
179 dvalue += lines[index] 184 dvalue += lines[index]
180 if index < len(lines) - 1 or len(dvalue) > 2048: 185 if index < len(lines) - 1 or len(dvalue) > 2048:
181 dvalue += "<...>" 186 dvalue += "<...>"
182 187
183 self.valueShort = dvalue 188 self.valueShort = dvalue
184 189
185 @property 190 @property
186 def absolutCount(self): 191 def absolutCount(self):
187 """ 192 """
188 Public property to get the total number of children. 193 Public property to get the total number of children.
189 194
190 @return total number of children 195 @return total number of children
191 @rtype int 196 @rtype int
192 """ 197 """
193 return self.childCount + self.methodCount 198 return self.childCount + self.methodCount
194 199
195 200
196 class VariablesModel(QAbstractItemModel): 201 class VariablesModel(QAbstractItemModel):
197 """ 202 """
198 Class implementing the data model for QTreeView. 203 Class implementing the data model for QTreeView.
199 204
200 @signal expand trigger QTreeView to expand given index 205 @signal expand trigger QTreeView to expand given index
201 """ 206 """
207
202 expand = pyqtSignal(QModelIndex) 208 expand = pyqtSignal(QModelIndex)
203 209
204 def __init__(self, treeView, globalScope): 210 def __init__(self, treeView, globalScope):
205 """ 211 """
206 Constructor 212 Constructor
207 213
208 @param treeView QTreeView showing the data 214 @param treeView QTreeView showing the data
209 @type VariablesViewer 215 @type VariablesViewer
210 @param globalScope flag indicating global (True) or local (False) 216 @param globalScope flag indicating global (True) or local (False)
211 variables 217 variables
212 @type bool 218 @type bool
213 """ 219 """
214 super().__init__() 220 super().__init__()
215 self.treeView = treeView 221 self.treeView = treeView
216 self.proxyModel = treeView.proxyModel 222 self.proxyModel = treeView.proxyModel
217 223
218 self.framenr = -1 224 self.framenr = -1
219 self.openItems = [] 225 self.openItems = []
220 self.closedItems = [] 226 self.closedItems = []
221 227
222 visibility = self.tr("Globals") if globalScope else self.tr("Locals") 228 visibility = self.tr("Globals") if globalScope else self.tr("Locals")
223 self.rootNode = VariableItem(None, visibility, '', self.tr("Type"), 229 self.rootNode = VariableItem(
224 True, 0, self.tr("Value")) 230 None, visibility, "", self.tr("Type"), True, 0, self.tr("Value")
225 231 )
232
226 self.__globalScope = globalScope 233 self.__globalScope = globalScope
227 234
228 def clear(self, reset=False): 235 def clear(self, reset=False):
229 """ 236 """
230 Public method to clear the complete data model. 237 Public method to clear the complete data model.
231 238
232 @param reset flag to clear the expanded keys also 239 @param reset flag to clear the expanded keys also
233 @type bool 240 @type bool
234 """ 241 """
235 self.beginResetModel() 242 self.beginResetModel()
236 self.rootNode.children = [] 243 self.rootNode.children = []
239 self.rootNode.wasPopulated = False 246 self.rootNode.wasPopulated = False
240 if reset: 247 if reset:
241 self.openItems = [] 248 self.openItems = []
242 self.closedItems = [] 249 self.closedItems = []
243 self.endResetModel() 250 self.endResetModel()
244 251
245 def __findVariable(self, pathlist): 252 def __findVariable(self, pathlist):
246 """ 253 """
247 Private method to get to the given variable. 254 Private method to get to the given variable.
248 255
249 @param pathlist full path to the variable 256 @param pathlist full path to the variable
250 @type list of str 257 @type list of str
251 @return the found variable or None if it doesn't exist 258 @return the found variable or None if it doesn't exist
252 @rtype VariableItem or None 259 @rtype VariableItem or None
253 """ 260 """
254 node = self.rootNode 261 node = self.rootNode
255 262
256 for childName in pathlist or []: 263 for childName in pathlist or []:
257 for item in node.children: 264 for item in node.children:
258 if item.nameWithId == childName: 265 if item.nameWithId == childName:
259 node = item 266 node = item
260 break 267 break
261 else: 268 else:
262 return None 269 return None
263 270
264 return node # __IGNORE_WARNING_M834__ 271 return node # __IGNORE_WARNING_M834__
265 272
266 def showVariables(self, vlist, frmnr, pathlist=None): 273 def showVariables(self, vlist, frmnr, pathlist=None):
267 """ 274 """
268 Public method to update the data model of variable in pathlist. 275 Public method to update the data model of variable in pathlist.
269 276
270 @param vlist the list of variables to be displayed. Each 277 @param vlist the list of variables to be displayed. Each
271 list entry is a tuple of three values. 278 list entry is a tuple of three values.
272 <ul> 279 <ul>
273 <li>the variable name (string)</li> 280 <li>the variable name (string)</li>
274 <li>the variables type (string)</li> 281 <li>the variables type (string)</li>
285 else: 292 else:
286 itemStartIndex = -1 293 itemStartIndex = -1
287 if self.framenr != frmnr: 294 if self.framenr != frmnr:
288 self.clear() 295 self.clear()
289 self.framenr = frmnr 296 self.framenr = frmnr
290 297
291 parent = self.__findVariable(pathlist) 298 parent = self.__findVariable(pathlist)
292 if parent is None: 299 if parent is None:
293 return 300 return
294 301
295 parent.pendigFetch = False 302 parent.pendigFetch = False
296 303
297 if parent == self.rootNode: 304 if parent == self.rootNode:
298 parentIdx = QModelIndex() 305 parentIdx = QModelIndex()
299 parent.methodCount = len(vlist) 306 parent.methodCount = len(vlist)
300 else: 307 else:
301 row = parent.parent.children.index(parent) 308 row = parent.parent.children.index(parent)
302 parentIdx = self.createIndex(row, 0, parent) 309 parentIdx = self.createIndex(row, 0, parent)
303 310
304 if itemStartIndex == -3: 311 if itemStartIndex == -3:
305 # Item doesn't exist any more 312 # Item doesn't exist any more
306 parentIdx = self.parent(parentIdx) 313 parentIdx = self.parent(parentIdx)
307 self.beginRemoveRows(parentIdx, row, row) 314 self.beginRemoveRows(parentIdx, row, row)
308 del parent.parent.children[row] 315 del parent.parent.children[row]
309 self.endRemoveRows() 316 self.endRemoveRows()
310 parent.parent.childCount -= 1 317 parent.parent.childCount -= 1
311 return 318 return
312 319
313 elif itemStartIndex == -2: 320 elif itemStartIndex == -2:
314 parent.wasPopulated = True 321 parent.wasPopulated = True
315 parent.currentCount = parent.absolutCount 322 parent.currentCount = parent.absolutCount
316 parent.populated = True 323 parent.populated = True
317 # Remove items which are left over at the end of child list 324 # Remove items which are left over at the end of child list
318 self.__cleanupParentList(parent, parentIdx) 325 self.__cleanupParentList(parent, parentIdx)
319 return 326 return
320 327
321 elif itemStartIndex == -1: 328 elif itemStartIndex == -1:
322 parent.methodCount = len(vlist) 329 parent.methodCount = len(vlist)
323 idx = max(parent.currentCount, 0) 330 idx = max(parent.currentCount, 0)
324 parent.currentCount = idx + len(vlist) 331 parent.currentCount = idx + len(vlist)
325 parent.populated = True 332 parent.populated = True
326 else: 333 else:
327 idx = itemStartIndex 334 idx = itemStartIndex
328 parent.currentCount = idx + len(vlist) 335 parent.currentCount = idx + len(vlist)
329 336
330 # Now update the table 337 # Now update the table
331 endIndex = idx + len(vlist) 338 endIndex = idx + len(vlist)
332 newChild = None 339 newChild = None
333 knownChildrenCount = len(parent.children) 340 knownChildrenCount = len(parent.children)
334 while idx < endIndex: 341 while idx < endIndex:
335 # Fetch next old item from last cycle 342 # Fetch next old item from last cycle
336 try: 343 try:
337 child = parent.children[idx] 344 child = parent.children[idx]
338 except IndexError: 345 except IndexError:
339 child = None 346 child = None
340 347
341 # Fetch possible new item 348 # Fetch possible new item
342 if not newChild and vlist: 349 if not newChild and vlist:
343 newChild = vlist.pop(0) 350 newChild = vlist.pop(0)
344 351
345 # Process parameters of new item 352 # Process parameters of new item
346 newItem = VariableItem(parent, *newChild) 353 newItem = VariableItem(parent, *newChild)
347 sort = newItem.sort 354 sort = newItem.sort
348 355
349 # Append or insert before already existing item 356 # Append or insert before already existing item
350 if child is None or newChild and sort < child.sort: 357 if child is None or newChild and sort < child.sort:
351 self.beginInsertRows(parentIdx, idx, idx) 358 self.beginInsertRows(parentIdx, idx, idx)
352 parent.children.insert(idx, newItem) 359 parent.children.insert(idx, newItem)
353 if knownChildrenCount <= idx and not parent.wasPopulated: 360 if knownChildrenCount <= idx and not parent.wasPopulated:
354 parent.newItems.add(newItem) 361 parent.newItems.add(newItem)
355 knownChildrenCount += 1 362 knownChildrenCount += 1
356 else: 363 else:
357 parent.changedItems.add(newItem) 364 parent.changedItems.add(newItem)
358 self.endInsertRows() 365 self.endInsertRows()
359 366
360 idx += 1 367 idx += 1
361 newChild = None 368 newChild = None
362 continue 369 continue
363 370
364 # Check if same name, type and afterwards value 371 # Check if same name, type and afterwards value
365 elif sort == child.sort and child.type == newItem.type: 372 elif sort == child.sort and child.type == newItem.type:
366 # Check if value has changed 373 # Check if value has changed
367 if child.value != newItem.value: 374 if child.value != newItem.value:
368 child.value = newItem.value 375 child.value = newItem.value
369 child.valueShort = newItem.valueShort 376 child.valueShort = newItem.valueShort
370 child.tooltip = newItem.tooltip 377 child.tooltip = newItem.tooltip
371 child.nameWithId = newItem.nameWithId 378 child.nameWithId = newItem.nameWithId
372 379
373 child.currentCount = -1 380 child.currentCount = -1
374 child.populated = False 381 child.populated = False
375 child.childCount = newItem.childCount 382 child.childCount = newItem.childCount
376 383
377 # Highlight item because it has changed 384 # Highlight item because it has changed
378 parent.changedItems.add(child) 385 parent.changedItems.add(child)
379 386
380 changedIndexStart = self.index(idx, 0, parentIdx) 387 changedIndexStart = self.index(idx, 0, parentIdx)
381 changedIndexEnd = self.index(idx, 2, parentIdx) 388 changedIndexEnd = self.index(idx, 2, parentIdx)
382 self.dataChanged.emit(changedIndexStart, changedIndexEnd) 389 self.dataChanged.emit(changedIndexStart, changedIndexEnd)
383 390
384 newChild = None 391 newChild = None
385 idx += 1 392 idx += 1
386 continue 393 continue
387 394
388 # Remove obsolete item 395 # Remove obsolete item
389 self.beginRemoveRows(parentIdx, idx, idx) 396 self.beginRemoveRows(parentIdx, idx, idx)
390 parent.children.remove(child) 397 parent.children.remove(child)
391 self.endRemoveRows() 398 self.endRemoveRows()
392 # idx stay unchanged 399 # idx stay unchanged
393 knownChildrenCount -= 1 400 knownChildrenCount -= 1
394 401
395 # Remove items which are left over at the end of child list 402 # Remove items which are left over at the end of child list
396 if itemStartIndex == -1: 403 if itemStartIndex == -1:
397 parent.wasPopulated = True 404 parent.wasPopulated = True
398 self.__cleanupParentList(parent, parentIdx) 405 self.__cleanupParentList(parent, parentIdx)
399 406
400 # Request data for any expanded node 407 # Request data for any expanded node
401 self.getMore() 408 self.getMore()
402 409
403 def __cleanupParentList(self, parent, parentIdx): 410 def __cleanupParentList(self, parent, parentIdx):
404 """ 411 """
405 Private method to remove items which are left over at the end of the 412 Private method to remove items which are left over at the end of the
406 child list. 413 child list.
407 414
408 @param parent to clean up 415 @param parent to clean up
409 @type VariableItem 416 @type VariableItem
410 @param parentIdx the parent index as QModelIndex 417 @param parentIdx the parent index as QModelIndex
411 @type QModelIndex 418 @type QModelIndex
412 """ 419 """
413 end = len(parent.children) 420 end = len(parent.children)
414 if end > parent.absolutCount: 421 if end > parent.absolutCount:
415 self.beginRemoveRows(parentIdx, parent.absolutCount, end) 422 self.beginRemoveRows(parentIdx, parent.absolutCount, end)
416 del parent.children[parent.absolutCount:] 423 del parent.children[parent.absolutCount :]
417 self.endRemoveRows() 424 self.endRemoveRows()
418 425
419 def resetModifiedMarker(self, parentIdx=QModelIndex(), pathlist=()): 426 def resetModifiedMarker(self, parentIdx=QModelIndex(), pathlist=()):
420 """ 427 """
421 Public method to remove the modified marker from changed items. 428 Public method to remove the modified marker from changed items.
422 429
423 @param parentIdx item to reset marker 430 @param parentIdx item to reset marker
424 @type QModelIndex 431 @type QModelIndex
425 @param pathlist full path to the variable 432 @param pathlist full path to the variable
426 @type list of str 433 @type list of str
427 """ 434 """
428 parent = (parentIdx.internalPointer() if parentIdx.isValid() 435 parent = parentIdx.internalPointer() if parentIdx.isValid() else self.rootNode
429 else self.rootNode) 436
430
431 parent.newItems.clear() 437 parent.newItems.clear()
432 parent.changedItems.clear() 438 parent.changedItems.clear()
433 439
434 pll = len(pathlist) 440 pll = len(pathlist)
435 posPaths = {x for x in self.openItems if len(x) > pll} 441 posPaths = {x for x in self.openItems if len(x) > pll}
436 posPaths |= {x for x in self.closedItems if len(x) > pll} 442 posPaths |= {x for x in self.closedItems if len(x) > pll}
437 posPaths = {x[pll] for x in posPaths if x[:pll] == pathlist} 443 posPaths = {x[pll] for x in posPaths if x[:pll] == pathlist}
438 444
439 if posPaths: 445 if posPaths:
440 for child in parent.children: 446 for child in parent.children:
441 if ( 447 if (
442 child.hasChildren and 448 child.hasChildren
443 child.nameWithId in posPaths and 449 and child.nameWithId in posPaths
444 child.currentCount >= 0 450 and child.currentCount >= 0
445 ): 451 ):
446 # Discard loaded elements and refresh if still expanded 452 # Discard loaded elements and refresh if still expanded
447 child.currentCount = -1 453 child.currentCount = -1
448 child.populated = False 454 child.populated = False
449 row = parent.children.index(child) 455 row = parent.children.index(child)
450 newParentIdx = self.index(row, 0, parentIdx) 456 newParentIdx = self.index(row, 0, parentIdx)
451 self.resetModifiedMarker( 457 self.resetModifiedMarker(
452 newParentIdx, pathlist + (child.nameWithId,)) 458 newParentIdx, pathlist + (child.nameWithId,)
453 459 )
460
454 self.closedItems = [] 461 self.closedItems = []
455 462
456 # Little quirk: Refresh all visible items to clear the changed marker 463 # Little quirk: Refresh all visible items to clear the changed marker
457 if parentIdx == QModelIndex(): 464 if parentIdx == QModelIndex():
458 self.rootNode.currentCount = -1 465 self.rootNode.currentCount = -1
459 self.rootNode.populated = False 466 self.rootNode.populated = False
460 idxStart = self.index(0, 0, QModelIndex()) 467 idxStart = self.index(0, 0, QModelIndex())
461 idxEnd = self.index(0, 2, QModelIndex()) 468 idxEnd = self.index(0, 2, QModelIndex())
462 self.dataChanged.emit(idxStart, idxEnd) 469 self.dataChanged.emit(idxStart, idxEnd)
463 470
464 def columnCount(self, parent=QModelIndex()): 471 def columnCount(self, parent=QModelIndex()):
465 """ 472 """
466 Public method to get the column count. 473 Public method to get the column count.
467 474
468 @param parent the model parent 475 @param parent the model parent
469 @type QModelIndex 476 @type QModelIndex
470 @return number of columns 477 @return number of columns
471 @rtype int 478 @rtype int
472 """ 479 """
473 return 3 480 return 3
474 481
475 def rowCount(self, parent=QModelIndex()): 482 def rowCount(self, parent=QModelIndex()):
476 """ 483 """
477 Public method to get the row count. 484 Public method to get the row count.
478 485
479 @param parent the model parent 486 @param parent the model parent
480 @type QModelIndex 487 @type QModelIndex
481 @return number of rows 488 @return number of rows
482 @rtype int 489 @rtype int
483 """ 490 """
484 node = parent.internalPointer() if parent.isValid() else self.rootNode 491 node = parent.internalPointer() if parent.isValid() else self.rootNode
485 492
486 return len(node.children) 493 return len(node.children)
487 494
488 def flags(self, index): 495 def flags(self, index):
489 """ 496 """
490 Public method to get the item flags. 497 Public method to get the item flags.
491 498
492 @param index of item 499 @param index of item
493 @type QModelIndex 500 @type QModelIndex
494 @return item flags 501 @return item flags
495 @rtype QtCore.Qt.ItemFlag 502 @rtype QtCore.Qt.ItemFlag
496 """ 503 """
497 if not index.isValid(): 504 if not index.isValid():
498 return Qt.ItemFlag.NoItemFlags 505 return Qt.ItemFlag.NoItemFlags
499 506
500 return Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable 507 return Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable
501 508
502 def hasChildren(self, parent=QModelIndex()): 509 def hasChildren(self, parent=QModelIndex()):
503 """ 510 """
504 Public method to get a flag if parent has children. 511 Public method to get a flag if parent has children.
505 512
506 @param parent the model parent 513 @param parent the model parent
507 @type QModelIndex 514 @type QModelIndex
508 @return flag indicating parent has children 515 @return flag indicating parent has children
509 @rtype bool 516 @rtype bool
510 """ 517 """
511 if not parent.isValid(): 518 if not parent.isValid():
512 return self.rootNode.children != [] 519 return self.rootNode.children != []
513 520
514 return parent.internalPointer().hasChildren 521 return parent.internalPointer().hasChildren
515 522
516 def index(self, row, column, parent=QModelIndex()): 523 def index(self, row, column, parent=QModelIndex()):
517 """ 524 """
518 Public method to get the index of item at row:column of parent. 525 Public method to get the index of item at row:column of parent.
519 526
520 @param row number of rows 527 @param row number of rows
521 @type int 528 @type int
522 @param column number of columns 529 @param column number of columns
523 @type int 530 @type int
524 @param parent the model parent 531 @param parent the model parent
526 @return new model index for child 533 @return new model index for child
527 @rtype QModelIndex 534 @rtype QModelIndex
528 """ 535 """
529 if not self.hasIndex(row, column, parent): 536 if not self.hasIndex(row, column, parent):
530 return QModelIndex() 537 return QModelIndex()
531 538
532 node = parent.internalPointer() if parent.isValid() else self.rootNode 539 node = parent.internalPointer() if parent.isValid() else self.rootNode
533 540
534 return self.createIndex(row, column, node.children[row]) 541 return self.createIndex(row, column, node.children[row])
535 542
536 def parent(self, child): 543 def parent(self, child):
537 """ 544 """
538 Public method to get the parent of the given child. 545 Public method to get the parent of the given child.
539 546
540 @param child the model child node 547 @param child the model child node
541 @type QModelIndex 548 @type QModelIndex
542 @return new model index for parent 549 @return new model index for parent
543 @rtype QModelIndex 550 @rtype QModelIndex
544 """ 551 """
546 return QModelIndex() 553 return QModelIndex()
547 554
548 childNode = child.internalPointer() 555 childNode = child.internalPointer()
549 if childNode == self.rootNode: 556 if childNode == self.rootNode:
550 return QModelIndex() 557 return QModelIndex()
551 558
552 parentNode = childNode.parent 559 parentNode = childNode.parent
553 560
554 if parentNode == self.rootNode: 561 if parentNode == self.rootNode:
555 return QModelIndex() 562 return QModelIndex()
556 563
557 row = parentNode.parent.children.index(parentNode) 564 row = parentNode.parent.children.index(parentNode)
558 return self.createIndex(row, 0, parentNode) 565 return self.createIndex(row, 0, parentNode)
559 566
560 def data(self, index, role=Qt.ItemDataRole.DisplayRole): 567 def data(self, index, role=Qt.ItemDataRole.DisplayRole):
561 """ 568 """
562 Public method get the role data of item. 569 Public method get the role data of item.
563 570
564 @param index the model index 571 @param index the model index
565 @type QModelIndex 572 @type QModelIndex
566 @param role the requested data role 573 @param role the requested data role
567 @type QtCore.Qt.ItemDataRole 574 @type QtCore.Qt.ItemDataRole
568 @return role data of item 575 @return role data of item
569 @rtype Any 576 @rtype Any
570 """ 577 """
571 if not index.isValid() or index.row() < 0: 578 if not index.isValid() or index.row() < 0:
572 return None 579 return None
573 580
574 node = index.internalPointer() 581 node = index.internalPointer()
575 column = index.column() 582 column = index.column()
576 583
577 if role in ( 584 if role in (Qt.ItemDataRole.DisplayRole, SORT_ROLE, Qt.ItemDataRole.EditRole):
578 Qt.ItemDataRole.DisplayRole, SORT_ROLE, Qt.ItemDataRole.EditRole
579 ):
580 try: 585 try:
581 if column == 0: 586 if column == 0:
582 # Sort first column with values from third column 587 # Sort first column with values from third column
583 if role == SORT_ROLE: 588 if role == SORT_ROLE:
584 return node.sort 589 return node.sort
585 return node.name + node.indicator 590 return node.name + node.indicator
586 else: 591 else:
587 return { 592 return {1: node.valueShort, 2: node.type, 3: node.sort}.get(column)
588 1: node.valueShort,
589 2: node.type,
590 3: node.sort
591 }.get(column)
592 except AttributeError: 593 except AttributeError:
593 return ('None', '', '', '')[column] 594 return ("None", "", "", "")[column]
594 595
595 elif role == Qt.ItemDataRole.BackgroundRole: 596 elif role == Qt.ItemDataRole.BackgroundRole:
596 if node in node.parent.changedItems: 597 if node in node.parent.changedItems:
597 return self.__bgColorChanged 598 return self.__bgColorChanged
598 elif node in node.parent.newItems: 599 elif node in node.parent.newItems:
599 return self.__bgColorNew 600 return self.__bgColorNew
600 601
601 elif role == Qt.ItemDataRole.ToolTipRole: 602 elif role == Qt.ItemDataRole.ToolTipRole:
602 if column == 0: 603 if column == 0:
603 tooltip = node.name + node.indicator 604 tooltip = node.name + node.indicator
604 elif column == 1: 605 elif column == 1:
605 tooltip = node.tooltip 606 tooltip = node.tooltip
610 else: 611 else:
611 return None 612 return None
612 613
613 if Qt.mightBeRichText(tooltip): 614 if Qt.mightBeRichText(tooltip):
614 tooltip = Utilities.html_encode(tooltip) 615 tooltip = Utilities.html_encode(tooltip)
615 616
616 if column == 0: 617 if column == 0:
617 indentation = self.treeView.indentation() 618 indentation = self.treeView.indentation()
618 indentCount = 0 619 indentCount = 0
619 currentNode = node 620 currentNode = node
620 while currentNode.parent: 621 while currentNode.parent:
621 indentCount += 1 622 indentCount += 1
622 currentNode = currentNode.parent 623 currentNode = currentNode.parent
623 624
624 indentation *= indentCount 625 indentation *= indentCount
625 else: 626 else:
626 indentation = 0 627 indentation = 0
627 # Check if text is longer than available space 628 # Check if text is longer than available space
628 fontMetrics = QFontMetrics(self.treeView.font()) 629 fontMetrics = QFontMetrics(self.treeView.font())
634 header = self.treeView.header() 635 header = self.treeView.header()
635 if textSize >= header.sectionSize(column): 636 if textSize >= header.sectionSize(column):
636 return tooltip 637 return tooltip
637 else: 638 else:
638 QToolTip.hideText() 639 QToolTip.hideText()
639 640
640 return None 641 return None
641 642
642 def headerData(self, section, orientation, 643 def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole):
643 role=Qt.ItemDataRole.DisplayRole):
644 """ 644 """
645 Public method get the header names. 645 Public method get the header names.
646 646
647 @param section the header section (row/column) 647 @param section the header section (row/column)
648 @type int 648 @type int
649 @param orientation the header's orientation 649 @param orientation the header's orientation
650 @type QtCore.Qt.Orientation 650 @type QtCore.Qt.Orientation
651 @param role the requested data role 651 @param role the requested data role
652 @type QtCore.Qt.ItemDataRole 652 @type QtCore.Qt.ItemDataRole
653 @return header name 653 @return header name
654 @rtype str or None 654 @rtype str or None
655 """ 655 """
656 if ( 656 if (
657 role != Qt.ItemDataRole.DisplayRole or 657 role != Qt.ItemDataRole.DisplayRole
658 orientation != Qt.Orientation.Horizontal 658 or orientation != Qt.Orientation.Horizontal
659 ): 659 ):
660 return None 660 return None
661 661
662 return { 662 return {
663 0: self.rootNode.name, 663 0: self.rootNode.name,
664 1: self.rootNode.value, 664 1: self.rootNode.value,
665 2: self.rootNode.type, 665 2: self.rootNode.type,
666 3: self.rootNode.sort 666 3: self.rootNode.sort,
667 }.get(section) 667 }.get(section)
668 668
669 def __findPendingItem(self, parent=None, pathlist=()): 669 def __findPendingItem(self, parent=None, pathlist=()):
670 """ 670 """
671 Private method to find the next item to request data from debugger. 671 Private method to find the next item to request data from debugger.
672 672
673 @param parent the model parent 673 @param parent the model parent
674 @type VariableItem 674 @type VariableItem
675 @param pathlist full path to the variable 675 @param pathlist full path to the variable
676 @type list of str 676 @type list of str
677 @return next item index to request data from debugger 677 @return next item index to request data from debugger
678 @rtype QModelIndex 678 @rtype QModelIndex
679 """ 679 """
680 if parent is None: 680 if parent is None:
681 parent = self.rootNode 681 parent = self.rootNode
682 682
683 for child in parent.children: 683 for child in parent.children:
684 if not child.hasChildren: 684 if not child.hasChildren:
685 continue 685 continue
686 686
687 if pathlist + (child.nameWithId,) in self.openItems: 687 if pathlist + (child.nameWithId,) in self.openItems:
688 if child.populated: 688 if child.populated:
689 index = None 689 index = None
690 else: 690 else:
691 idx = parent.children.index(child) 691 idx = parent.children.index(child)
692 index = self.createIndex(idx, 0, child) 692 index = self.createIndex(idx, 0, child)
693 self.expand.emit(index) 693 self.expand.emit(index)
694 694
695 if child.currentCount < 0: 695 if child.currentCount < 0:
696 return index 696 return index
697 697
698 possibleIndex = self.__findPendingItem( 698 possibleIndex = self.__findPendingItem(
699 child, pathlist + (child.nameWithId,)) 699 child, pathlist + (child.nameWithId,)
700 700 )
701
701 if (possibleIndex or index) is None: 702 if (possibleIndex or index) is None:
702 continue 703 continue
703 704
704 return possibleIndex or index 705 return possibleIndex or index
705 706
706 return None 707 return None
707 708
708 def getMore(self): 709 def getMore(self):
709 """ 710 """
710 Public method to fetch the next variable from debugger. 711 Public method to fetch the next variable from debugger.
711 """ 712 """
712 # step 1: find expanded but not populated items 713 # step 1: find expanded but not populated items
713 item = self.__findPendingItem() 714 item = self.__findPendingItem()
714 if not item or not item.isValid(): 715 if not item or not item.isValid():
715 return 716 return
716 717
717 # step 2: check if data has to be retrieved 718 # step 2: check if data has to be retrieved
718 node = item.internalPointer() 719 node = item.internalPointer()
719 lastVisibleItem = self.index(node.currentCount - 1, 0, item) 720 lastVisibleItem = self.index(node.currentCount - 1, 0, item)
720 lastVisibleItem = self.proxyModel.mapFromSource(lastVisibleItem) 721 lastVisibleItem = self.proxyModel.mapFromSource(lastVisibleItem)
721 rect = self.treeView.visualRect(lastVisibleItem) 722 rect = self.treeView.visualRect(lastVisibleItem)
722 if rect.y() > self.treeView.height() or node.pendigFetch: 723 if rect.y() > self.treeView.height() or node.pendigFetch:
723 return 724 return
724 725
725 node.pendigFetch = True 726 node.pendigFetch = True
726 # step 3: get a pathlist up to the requested variable 727 # step 3: get a pathlist up to the requested variable
727 pathlist = self.__buildTreePath(node) 728 pathlist = self.__buildTreePath(node)
728 # step 4: request the variable from the debugger 729 # step 4: request the variable from the debugger
729 variablesFilter = ericApp().getObject("DebugUI").variablesFilter( 730 variablesFilter = (
730 self.__globalScope) 731 ericApp().getObject("DebugUI").variablesFilter(self.__globalScope)
732 )
731 ericApp().getObject("DebugServer").remoteClientVariable( 733 ericApp().getObject("DebugServer").remoteClientVariable(
732 ericApp().getObject("DebugUI").getSelectedDebuggerId(), 734 ericApp().getObject("DebugUI").getSelectedDebuggerId(),
733 self.__globalScope, variablesFilter, pathlist, self.framenr) 735 self.__globalScope,
734 736 variablesFilter,
737 pathlist,
738 self.framenr,
739 )
740
735 def setExpanded(self, index, state): 741 def setExpanded(self, index, state):
736 """ 742 """
737 Public method to set the expanded state of item. 743 Public method to set the expanded state of item.
738 744
739 @param index item to change expanded state 745 @param index item to change expanded state
740 @type QModelIndex 746 @type QModelIndex
741 @param state state of the item 747 @param state state of the item
742 @type bool 748 @type bool
743 """ 749 """
751 self.getMore() 757 self.getMore()
752 else: 758 else:
753 if pathlist in self.openItems: 759 if pathlist in self.openItems:
754 self.openItems.remove(pathlist) 760 self.openItems.remove(pathlist)
755 self.closedItems.append(pathlist) 761 self.closedItems.append(pathlist)
756 762
757 def __buildTreePath(self, parent): 763 def __buildTreePath(self, parent):
758 """ 764 """
759 Private method to build up a path from the root to parent. 765 Private method to build up a path from the root to parent.
760 766
761 @param parent item to build the path for 767 @param parent item to build the path for
762 @type VariableItem 768 @type VariableItem
763 @return list of names denoting the path from the root 769 @return list of names denoting the path from the root
764 @rtype tuple of str 770 @rtype tuple of str
765 """ 771 """
766 pathlist = [] 772 pathlist = []
767 773
768 # build up a path from the top to the item 774 # build up a path from the top to the item
769 while parent.parent: 775 while parent.parent:
770 pathlist.append(parent.nameWithId) 776 pathlist.append(parent.nameWithId)
771 parent = parent.parent 777 parent = parent.parent
772 778
773 pathlist.reverse() 779 pathlist.reverse()
774 return tuple(pathlist) 780 return tuple(pathlist)
775 781
776 def handlePreferencesChanged(self): 782 def handlePreferencesChanged(self):
777 """ 783 """
778 Public slot to handle the preferencesChanged signal. 784 Public slot to handle the preferencesChanged signal.
779 """ 785 """
780 self.__bgColorNew = QBrush(Preferences.getDebugger("BgColorNew")) 786 self.__bgColorNew = QBrush(Preferences.getDebugger("BgColorNew"))
781 self.__bgColorChanged = QBrush( 787 self.__bgColorChanged = QBrush(Preferences.getDebugger("BgColorChanged"))
782 Preferences.getDebugger("BgColorChanged")) 788
783
784 idxStart = self.index(0, 0, QModelIndex()) 789 idxStart = self.index(0, 0, QModelIndex())
785 idxEnd = self.index(0, 2, QModelIndex()) 790 idxEnd = self.index(0, 2, QModelIndex())
786 self.dataChanged.emit(idxStart, idxEnd) 791 self.dataChanged.emit(idxStart, idxEnd)
787 792
788 793
789 class VariablesProxyModel(QSortFilterProxyModel): 794 class VariablesProxyModel(QSortFilterProxyModel):
790 """ 795 """
791 Class for handling the sort operations. 796 Class for handling the sort operations.
792 """ 797 """
798
793 def __init__(self, parent=None): 799 def __init__(self, parent=None):
794 """ 800 """
795 Constructor 801 Constructor
796 802
797 @param parent the parent model index 803 @param parent the parent model index
798 @type QModelIndex 804 @type QModelIndex
799 """ 805 """
800 super().__init__(parent) 806 super().__init__(parent)
801 self.setSortRole(SORT_ROLE) 807 self.setSortRole(SORT_ROLE)
802 808
803 def hasChildren(self, parent): 809 def hasChildren(self, parent):
804 """ 810 """
805 Public method to get a flag if parent has children. 811 Public method to get a flag if parent has children.
806 812
807 The given model index has to be transformed to the underlying source 813 The given model index has to be transformed to the underlying source
808 model to get the correct result. 814 model to get the correct result.
809 815
810 @param parent the model parent 816 @param parent the model parent
811 @type QModelIndex 817 @type QModelIndex
812 @return flag if parent has children 818 @return flag if parent has children
813 @rtype bool 819 @rtype bool
814 """ 820 """
815 return self.sourceModel().hasChildren(self.mapToSource(parent)) 821 return self.sourceModel().hasChildren(self.mapToSource(parent))
816 822
817 def setExpanded(self, index, state): 823 def setExpanded(self, index, state):
818 """ 824 """
819 Public slot to get a flag if parent has children. 825 Public slot to get a flag if parent has children.
820 826
821 The given model index has to be transformed to the underlying source 827 The given model index has to be transformed to the underlying source
822 model to get the correct result. 828 model to get the correct result.
823 @param index item to change expanded state 829 @param index item to change expanded state
824 @type QModelIndex 830 @type QModelIndex
825 @param state state of the item 831 @param state state of the item
829 835
830 836
831 class VariablesViewer(QTreeView): 837 class VariablesViewer(QTreeView):
832 """ 838 """
833 Class implementing the variables viewer view. 839 Class implementing the variables viewer view.
834 840
835 This view is used to display the variables of the program being 841 This view is used to display the variables of the program being
836 debugged in a tree. Compound types will be shown with 842 debugged in a tree. Compound types will be shown with
837 their main entry first. Once the subtree has been expanded, the 843 their main entry first. Once the subtree has been expanded, the
838 individual entries will be shown. Double clicking an entry will 844 individual entries will be shown. Double clicking an entry will
839 expand or collapse the item, if it has children and the double click 845 expand or collapse the item, if it has children and the double click
840 was performed on the first column of the tree, otherwise it'll 846 was performed on the first column of the tree, otherwise it'll
841 popup a dialog showing the variables parameters in a more readable 847 popup a dialog showing the variables parameters in a more readable
842 form. This is especially useful for lengthy strings. 848 form. This is especially useful for lengthy strings.
843 849
844 This view has two modes for displaying the global and the local 850 This view has two modes for displaying the global and the local
845 variables. 851 variables.
846 852
847 @signal preferencesChanged() to inform model about new background colours 853 @signal preferencesChanged() to inform model about new background colours
848 """ 854 """
855
849 preferencesChanged = pyqtSignal() 856 preferencesChanged = pyqtSignal()
850 857
851 def __init__(self, viewer, globalScope, parent=None): 858 def __init__(self, viewer, globalScope, parent=None):
852 """ 859 """
853 Constructor 860 Constructor
854 861
855 @param viewer reference to the debug viewer object 862 @param viewer reference to the debug viewer object
856 @type DebugViewer 863 @type DebugViewer
857 @param globalScope flag indicating global (True) or local (False) 864 @param globalScope flag indicating global (True) or local (False)
858 variables 865 variables
859 @type bool 866 @type bool
860 @param parent the parent 867 @param parent the parent
861 @type QWidget 868 @type QWidget
862 """ 869 """
863 super().__init__(parent) 870 super().__init__(parent)
864 871
865 self.__debugViewer = viewer 872 self.__debugViewer = viewer
866 self.__globalScope = globalScope 873 self.__globalScope = globalScope
867 self.framenr = 0 874 self.framenr = 0
868 875
869 # Massive performance gain 876 # Massive performance gain
870 self.setUniformRowHeights(True) 877 self.setUniformRowHeights(True)
871 878
872 # Implements sorting and filtering 879 # Implements sorting and filtering
873 self.proxyModel = VariablesProxyModel() 880 self.proxyModel = VariablesProxyModel()
874 # Variable model implements the underlying data model 881 # Variable model implements the underlying data model
875 self.varModel = VariablesModel(self, globalScope) 882 self.varModel = VariablesModel(self, globalScope)
876 self.proxyModel.setSourceModel(self.varModel) 883 self.proxyModel.setSourceModel(self.varModel)
877 self.setModel(self.proxyModel) 884 self.setModel(self.proxyModel)
878 self.preferencesChanged.connect(self.varModel.handlePreferencesChanged) 885 self.preferencesChanged.connect(self.varModel.handlePreferencesChanged)
879 self.preferencesChanged.emit() # Force initialization of colors 886 self.preferencesChanged.emit() # Force initialization of colors
880 887
881 self.expanded.connect( 888 self.expanded.connect(lambda idx: self.proxyModel.setExpanded(idx, True))
882 lambda idx: self.proxyModel.setExpanded(idx, True)) 889 self.collapsed.connect(lambda idx: self.proxyModel.setExpanded(idx, False))
883 self.collapsed.connect( 890
884 lambda idx: self.proxyModel.setExpanded(idx, False))
885
886 self.setExpandsOnDoubleClick(False) 891 self.setExpandsOnDoubleClick(False)
887 self.doubleClicked.connect(self.__itemDoubleClicked) 892 self.doubleClicked.connect(self.__itemDoubleClicked)
888 893
889 self.varModel.expand.connect(self.__mdlRequestExpand) 894 self.varModel.expand.connect(self.__mdlRequestExpand)
890 895
891 self.setSortingEnabled(True) 896 self.setSortingEnabled(True)
892 self.setAlternatingRowColors(True) 897 self.setAlternatingRowColors(True)
893 self.setSelectionBehavior( 898 self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
894 QAbstractItemView.SelectionBehavior.SelectRows) 899
895
896 if self.__globalScope: 900 if self.__globalScope:
897 self.setWindowTitle(self.tr("Global Variables")) 901 self.setWindowTitle(self.tr("Global Variables"))
898 self.setWhatsThis(self.tr( 902 self.setWhatsThis(
899 """<b>The Global Variables Viewer Window</b>""" 903 self.tr(
900 """<p>This window displays the global variables""" 904 """<b>The Global Variables Viewer Window</b>"""
901 """ of the debugged program.</p>""" 905 """<p>This window displays the global variables"""
902 )) 906 """ of the debugged program.</p>"""
907 )
908 )
903 else: 909 else:
904 self.setWindowTitle(self.tr("Local Variables")) 910 self.setWindowTitle(self.tr("Local Variables"))
905 self.setWhatsThis(self.tr( 911 self.setWhatsThis(
906 """<b>The Local Variables Viewer Window</b>""" 912 self.tr(
907 """<p>This window displays the local variables""" 913 """<b>The Local Variables Viewer Window</b>"""
908 """ of the debugged program.</p>""" 914 """<p>This window displays the local variables"""
909 )) 915 """ of the debugged program.</p>"""
910 916 )
917 )
918
911 header = self.header() 919 header = self.header()
912 header.setSortIndicator(0, Qt.SortOrder.AscendingOrder) 920 header.setSortIndicator(0, Qt.SortOrder.AscendingOrder)
913 header.setSortIndicatorShown(True) 921 header.setSortIndicatorShown(True)
914 922
915 try: 923 try:
916 header.setSectionsClickable(True) 924 header.setSectionsClickable(True)
917 except Exception: 925 except Exception:
918 header.setClickable(True) 926 header.setClickable(True)
919 927
920 header.resizeSection(0, 130) # variable column 928 header.resizeSection(0, 130) # variable column
921 header.resizeSection(1, 180) # value column 929 header.resizeSection(1, 180) # value column
922 header.resizeSection(2, 50) # type column 930 header.resizeSection(2, 50) # type column
923 931
924 header.sortIndicatorChanged.connect(lambda *x: self.varModel.getMore()) 932 header.sortIndicatorChanged.connect(lambda *x: self.varModel.getMore())
925 933
926 self.__createPopupMenus() 934 self.__createPopupMenus()
927 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) 935 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
928 self.customContextMenuRequested.connect(self.__showContextMenu) 936 self.customContextMenuRequested.connect(self.__showContextMenu)
929 937
930 self.resortEnabled = True 938 self.resortEnabled = True
931 939
932 def showVariables(self, vlist, frmnr): 940 def showVariables(self, vlist, frmnr):
933 """ 941 """
934 Public method to show variables in a list. 942 Public method to show variables in a list.
935 943
936 @param vlist the list of variables to be displayed. Each 944 @param vlist the list of variables to be displayed. Each
937 list entry is a tuple of three values. 945 list entry is a tuple of three values.
938 <ul> 946 <ul>
939 <li>the variable name (string)</li> 947 <li>the variable name (string)</li>
940 <li>the variables type (string)</li> 948 <li>the variables type (string)</li>
944 @param frmnr frame number (0 is the current frame) 952 @param frmnr frame number (0 is the current frame)
945 @type int 953 @type int
946 """ 954 """
947 self.varModel.resetModifiedMarker() 955 self.varModel.resetModifiedMarker()
948 self.varModel.showVariables(vlist, frmnr) 956 self.varModel.showVariables(vlist, frmnr)
949 957
950 def showVariable(self, vlist): 958 def showVariable(self, vlist):
951 """ 959 """
952 Public method to show variables in a list. 960 Public method to show variables in a list.
953 961
954 @param vlist the list of subitems to be displayed. 962 @param vlist the list of subitems to be displayed.
955 The first element gives the path of the 963 The first element gives the path of the
956 parent variable. Each other list entry is 964 parent variable. Each other list entry is
957 a tuple of three values. 965 a tuple of three values.
958 <ul> 966 <ul>
961 <li>the variables value (string)</li> 969 <li>the variables value (string)</li>
962 </ul> 970 </ul>
963 @type list 971 @type list
964 """ 972 """
965 self.varModel.showVariables(vlist[1:], 0, vlist[0]) 973 self.varModel.showVariables(vlist[1:], 0, vlist[0])
966 974
967 def handleResetUI(self): 975 def handleResetUI(self):
968 """ 976 """
969 Public method to reset the VariablesViewer. 977 Public method to reset the VariablesViewer.
970 """ 978 """
971 self.varModel.clear(True) 979 self.varModel.clear(True)
972 980
973 def verticalScrollbarValueChanged(self, value): 981 def verticalScrollbarValueChanged(self, value):
974 """ 982 """
975 Public slot informing about the scrollbar change. 983 Public slot informing about the scrollbar change.
976 984
977 @param value current value of the vertical scrollbar 985 @param value current value of the vertical scrollbar
978 @type int 986 @type int
979 """ 987 """
980 self.varModel.getMore() 988 self.varModel.getMore()
981 super().verticalScrollbarValueChanged(value) 989 super().verticalScrollbarValueChanged(value)
982 990
983 def resizeEvent(self, event): 991 def resizeEvent(self, event):
984 """ 992 """
985 Protected slot informing about the widget size change. 993 Protected slot informing about the widget size change.
986 994
987 @param event information 995 @param event information
988 @type QResizeEvent 996 @type QResizeEvent
989 """ 997 """
990 self.varModel.getMore() 998 self.varModel.getMore()
991 super().resizeEvent(event) 999 super().resizeEvent(event)
992 1000
993 def __itemDoubleClicked(self, index): 1001 def __itemDoubleClicked(self, index):
994 """ 1002 """
995 Private method called if an item was double clicked. 1003 Private method called if an item was double clicked.
996 1004
997 @param index the double clicked item 1005 @param index the double clicked item
998 @type QModelIndex 1006 @type QModelIndex
999 """ 1007 """
1000 node = self.proxyModel.mapToSource(index).internalPointer() 1008 node = self.proxyModel.mapToSource(index).internalPointer()
1001 if node.hasChildren and index.column() == 0: 1009 if node.hasChildren and index.column() == 0:
1002 state = self.isExpanded(index) 1010 state = self.isExpanded(index)
1003 self.setExpanded(index, not state) 1011 self.setExpanded(index, not state)
1004 else: 1012 else:
1005 self.__showVariableDetails(index) 1013 self.__showVariableDetails(index)
1006 1014
1007 def __mdlRequestExpand(self, modelIndex): 1015 def __mdlRequestExpand(self, modelIndex):
1008 """ 1016 """
1009 Private method to inform the view about items to be expand. 1017 Private method to inform the view about items to be expand.
1010 1018
1011 @param modelIndex the model index 1019 @param modelIndex the model index
1012 @type QModelIndex 1020 @type QModelIndex
1013 """ 1021 """
1014 index = self.proxyModel.mapFromSource(modelIndex) 1022 index = self.proxyModel.mapFromSource(modelIndex)
1015 self.expand(index) 1023 self.expand(index)
1016 1024
1017 def __createPopupMenus(self): 1025 def __createPopupMenus(self):
1018 """ 1026 """
1019 Private method to generate the popup menus. 1027 Private method to generate the popup menus.
1020 """ 1028 """
1021 self.menu = QMenu() 1029 self.menu = QMenu()
1026 self.menu.addAction(self.tr("Collapse All"), self.collapseAll) 1034 self.menu.addAction(self.tr("Collapse All"), self.collapseAll)
1027 self.menu.addSeparator() 1035 self.menu.addSeparator()
1028 self.menu.addAction(self.tr("Refresh"), self.__refreshView) 1036 self.menu.addAction(self.tr("Refresh"), self.__refreshView)
1029 self.menu.addSeparator() 1037 self.menu.addSeparator()
1030 self.menu.addAction(self.tr("Configure..."), self.__configure) 1038 self.menu.addAction(self.tr("Configure..."), self.__configure)
1031 self.menu.addAction(self.tr("Variables Type Filter..."), 1039 self.menu.addAction(self.tr("Variables Type Filter..."), self.__configureFilter)
1032 self.__configureFilter) 1040
1033
1034 self.backMenu = QMenu() 1041 self.backMenu = QMenu()
1035 self.backMenu.addAction(self.tr("Refresh"), self.__refreshView) 1042 self.backMenu.addAction(self.tr("Refresh"), self.__refreshView)
1036 self.backMenu.addSeparator() 1043 self.backMenu.addSeparator()
1037 self.backMenu.addAction(self.tr("Configure..."), self.__configure) 1044 self.backMenu.addAction(self.tr("Configure..."), self.__configure)
1038 self.backMenu.addAction(self.tr("Variables Type Filter..."), 1045 self.backMenu.addAction(
1039 self.__configureFilter) 1046 self.tr("Variables Type Filter..."), self.__configureFilter
1040 1047 )
1048
1041 def __showContextMenu(self, coord): 1049 def __showContextMenu(self, coord):
1042 """ 1050 """
1043 Private slot to show the context menu. 1051 Private slot to show the context menu.
1044 1052
1045 @param coord the position of the mouse pointer 1053 @param coord the position of the mouse pointer
1046 @type QPoint 1054 @type QPoint
1047 """ 1055 """
1048 gcoord = self.mapToGlobal(coord) 1056 gcoord = self.mapToGlobal(coord)
1049 if self.indexAt(coord).isValid(): 1057 if self.indexAt(coord).isValid():
1050 self.menu.popup(gcoord) 1058 self.menu.popup(gcoord)
1051 else: 1059 else:
1052 self.backMenu.popup(gcoord) 1060 self.backMenu.popup(gcoord)
1053 1061
1054 def __expandChildren(self): 1062 def __expandChildren(self):
1055 """ 1063 """
1056 Private slot to expand all child items of current parent. 1064 Private slot to expand all child items of current parent.
1057 """ 1065 """
1058 index = self.currentIndex() 1066 index = self.currentIndex()
1061 if child.hasChildren: 1069 if child.hasChildren:
1062 row = node.children.index(child) 1070 row = node.children.index(child)
1063 idx = self.varModel.createIndex(row, 0, child) 1071 idx = self.varModel.createIndex(row, 0, child)
1064 idx = self.proxyModel.mapFromSource(idx) 1072 idx = self.proxyModel.mapFromSource(idx)
1065 self.expand(idx) 1073 self.expand(idx)
1066 1074
1067 def __collapseChildren(self): 1075 def __collapseChildren(self):
1068 """ 1076 """
1069 Private slot to collapse all child items of current parent. 1077 Private slot to collapse all child items of current parent.
1070 """ 1078 """
1071 index = self.currentIndex() 1079 index = self.currentIndex()
1074 row = node.children.index(child) 1082 row = node.children.index(child)
1075 idx = self.varModel.createIndex(row, 0, child) 1083 idx = self.varModel.createIndex(row, 0, child)
1076 idx = self.proxyModel.mapFromSource(idx) 1084 idx = self.proxyModel.mapFromSource(idx)
1077 if self.isExpanded(idx): 1085 if self.isExpanded(idx):
1078 self.collapse(idx) 1086 self.collapse(idx)
1079 1087
1080 def __refreshView(self): 1088 def __refreshView(self):
1081 """ 1089 """
1082 Private slot to refresh the view. 1090 Private slot to refresh the view.
1083 """ 1091 """
1084 if self.__globalScope: 1092 if self.__globalScope:
1085 self.__debugViewer.setGlobalsFilter() 1093 self.__debugViewer.setGlobalsFilter()
1086 else: 1094 else:
1087 self.__debugViewer.setLocalsFilter() 1095 self.__debugViewer.setLocalsFilter()
1088 1096
1089 def __showDetails(self): 1097 def __showDetails(self):
1090 """ 1098 """
1091 Private slot to show details about the selected variable. 1099 Private slot to show details about the selected variable.
1092 """ 1100 """
1093 idx = self.currentIndex() 1101 idx = self.currentIndex()
1094 self.__showVariableDetails(idx) 1102 self.__showVariableDetails(idx)
1095 1103
1096 def __showVariableDetails(self, index): 1104 def __showVariableDetails(self, index):
1097 """ 1105 """
1098 Private method to show details about a variable. 1106 Private method to show details about a variable.
1099 1107
1100 @param index reference to the variable item 1108 @param index reference to the variable item
1101 @type QModelIndex 1109 @type QModelIndex
1102 """ 1110 """
1103 node = self.proxyModel.mapToSource(index).internalPointer() 1111 node = self.proxyModel.mapToSource(index).internalPointer()
1104 1112
1105 val = node.value 1113 val = node.value
1106 vtype = node.type 1114 vtype = node.type
1107 name = node.name 1115 name = node.name
1108 1116
1109 par = node.parent 1117 par = node.parent
1110 nlist = [name] 1118 nlist = [name]
1111 1119
1112 # build up the fully qualified name 1120 # build up the fully qualified name
1113 while par.parent is not None: 1121 while par.parent is not None:
1114 pname = par.name 1122 pname = par.name
1115 if par.indicator: 1123 if par.indicator:
1116 if nlist[0].endswith("."): 1124 if nlist[0].endswith("."):
1117 nlist[0] = '[{0}].'.format(nlist[0][:-1]) 1125 nlist[0] = "[{0}].".format(nlist[0][:-1])
1118 else: 1126 else:
1119 nlist[0] = '[{0}]'.format(nlist[0]) 1127 nlist[0] = "[{0}]".format(nlist[0])
1120 nlist.insert(0, pname) 1128 nlist.insert(0, pname)
1121 else: 1129 else:
1122 if par.type == "django.MultiValueDict": 1130 if par.type == "django.MultiValueDict":
1123 nlist[0] = 'getlist({0})'.format(nlist[0]) 1131 nlist[0] = "getlist({0})".format(nlist[0])
1124 elif par.type == "numpy.ndarray": 1132 elif par.type == "numpy.ndarray":
1125 if nlist and nlist[0][0].isalpha(): 1133 if nlist and nlist[0][0].isalpha():
1126 if nlist[0] in ["min", "max", "mean"]: 1134 if nlist[0] in ["min", "max", "mean"]:
1127 nlist[0] = ".{0}()".format(nlist[0]) 1135 nlist[0] = ".{0}()".format(nlist[0])
1128 else: 1136 else:
1129 nlist[0] = ".{0}".format(nlist[0]) 1137 nlist[0] = ".{0}".format(nlist[0])
1130 nlist.insert(0, pname) 1138 nlist.insert(0, pname)
1131 else: 1139 else:
1132 nlist.insert(0, '{0}.'.format(pname)) 1140 nlist.insert(0, "{0}.".format(pname))
1133 par = par.parent 1141 par = par.parent
1134 1142
1135 name = ''.join(nlist) 1143 name = "".join(nlist)
1136 # now show the dialog 1144 # now show the dialog
1137 from .VariableDetailDialog import VariableDetailDialog 1145 from .VariableDetailDialog import VariableDetailDialog
1146
1138 dlg = VariableDetailDialog(name, vtype, val) 1147 dlg = VariableDetailDialog(name, vtype, val)
1139 dlg.exec() 1148 dlg.exec()
1140 1149
1141 def __configure(self): 1150 def __configure(self):
1142 """ 1151 """
1143 Private method to open the configuration dialog. 1152 Private method to open the configuration dialog.
1144 """ 1153 """
1145 ericApp().getObject("UserInterface").showPreferences( 1154 ericApp().getObject("UserInterface").showPreferences("debuggerGeneralPage")
1146 "debuggerGeneralPage") 1155
1147
1148 def __configureFilter(self): 1156 def __configureFilter(self):
1149 """ 1157 """
1150 Private method to open the variables filter dialog. 1158 Private method to open the variables filter dialog.
1151 """ 1159 """
1152 ericApp().getObject("DebugUI").dbgFilterAct.triggered.emit() 1160 ericApp().getObject("DebugUI").dbgFilterAct.triggered.emit()
1161
1153 1162
1154 # 1163 #
1155 # eflag: noqa = M822 1164 # eflag: noqa = M822

eric ide

mercurial