This is really puzzling me:
I have a subclass of QLayout based on the flow example in the documentation. I've created a new widget that uses it, and for some reason, now Qt is trying to access itemAt with an index >= count() which is resulting in an exception. It is not calling count() before it asks for each item My code is not calling itemAt() anywhere. Here is the part of the widget class:
Qt Code:
  1. class DElementWidget(QWidget):
  2. def __init__(self, element, *args):
  3. super(DElementWidget, self).__init__(*args)
  4. self.element = element
  5. self.view_mode = True
  6. self.format = ['title', 'name', ('string', '='), 'value']
  7.  
  8. layout = QGridLayout()
  9. layout.setSpacing(2)
  10. layout.setMargin(2)
  11.  
  12. Omitted, added 4 buttons here
  13.  
  14. layout.setColumnMinimumWidth(4, 30)
  15. layout.setColumnStretch(4, 1)
  16.  
  17. format_holder_w = QWidget()
  18.  
  19. self.format_holder = DFlowLayout(2, 3, 3)
  20. format_holder_w.setLayout(self.format_holder)
  21. for index in range(len(self.format)):
  22. w = self.widget_for(index)
  23. self.format_holder.addWidget(w)
  24.  
  25. if isinstance(self.element.value, Integer):
  26. self.format_holder.addWidget(DElementWidget(DObject(RealNumber(6)*RealNumber(7), parent=self.element)))
  27.  
  28. scrollarea = QScrollArea()
  29. scrollarea.setWidget(format_holder_w)
  30. layout.addWidget(scrollarea, 1, 0, 1, 5)
  31.  
  32. self.setLayout(layout)
  33.  
  34. def widget_for(self, index):
  35. return QLabel(unicode(self.format[index]))
To copy to clipboard, switch view to plain text mode 
Here are parts of the layout:
Qt Code:
  1. class DFlowLayout(QLayout):
  2. '''Translated from http://doc.qt.nokia.com/latest/layouts-flowlayout.html'''
  3. def __init__(self, margins = -1, hspace=-1, vspace=-1, *args):
  4. super(DFlowLayout, self).__init__(*args)
  5. self.setContentsMargins(margins, margins, margins, margins)
  6. self._hspace = hspace
  7. self._vspace = vspace
  8. self.valign = Qt.AlignCenter
  9. self.halign = Qt.AlignLeft
  10. self.items = []
  11.  
  12. def addItem(self, item):
  13. print 'added', item
  14. self.items.append(item)
  15.  
  16. def replaceItem(self, old, item):
  17. ....
  18. Omitted, not used
  19. ....
  20.  
  21. def count(self):
  22. print len(self.items)
  23. return len(self.items)
  24.  
  25. def horizontalSpacing(self):
  26. if self._hspace >= 0: return self._hspace
  27. else: return self.smartSpacing(QStyle.PM_LayoutHorizontalSpacing)
  28.  
  29. def takeAt(self, index):
  30. v = self.items[index]
  31. self.items = self.items[:index] + self.items[index+1:]
  32. return v
  33.  
  34. def itemAt(self, index):
  35. print 'get', index
  36. return self.items[index]
  37.  
  38. def expandingDirections(self):
  39. return Qt.Orientation()
  40.  
  41. def setGeometry(self, rect):
  42. QLayout.setGeometry(self, rect)
  43. self.doLayout(rect)
  44.  
  45. def lines(self, width = -1):
  46. ...
  47. Omitted, returns the wrapped lines and their dimensions
  48. ....
  49. return result, widths, heights
  50.  
  51. def hasHeightForWidth(self):
  52. return True
  53.  
  54. def heightForWidth(self, width):
  55. return sum(self.lines(width - 2*self.margin())[2]) + 2*self.margin()
  56.  
  57. def sizeHint(self):
  58. return self.minimumSize()
  59.  
  60. def minimumSize(self):
  61. res = self.lines()
  62. if len(res[0]) == 0:
  63. return QSize()
  64. width = max(res[1]) + 2*self.margin()
  65. height = sum(res[2]) + 2*self.margin()
  66. #print height
  67. return QSize(width, height)
  68.  
  69. def doLayout(self, rect):
  70. .....
  71. Omitted, places lines
  72. .....
  73.  
  74. def verticalSpacing(self):
  75. if self._vspace >= 0: return self._vspace
  76. else: return self.smartSpacing(QStyle.PM_LayoutVerticalSpacing)
  77.  
  78. def smartSpacing(self, thingy):
  79. parent = self.parent()
  80. if parent == None:
  81. return None
  82. elif parent.isWidgetType():
  83. return thingy.style().pixelMetric(thingy, 0, thingy)
  84. else:
  85. return parent.spacing()
To copy to clipboard, switch view to plain text mode 
Adding widgets to format_holder_w but not the layout does not change which items are accessed. It always tries to get one more item than exists in the array, except when the calls are preceeded by a call to count() in which case there is no exception. This class has worked before without errors. I'm really at a loss here, WTF is going on?