eric6/Debugger/VariablesViewer.py

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

eric ide

mercurial