PDA

View Full Version : Index out of bounds in custom QLayout subclass



space_otter
5th October 2011, 03:38
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:


class DElementWidget(QWidget):
def __init__(self, element, *args):
super(DElementWidget, self).__init__(*args)
self.element = element
self.view_mode = True
self.format = ['title', 'name', ('string', '='), 'value']

layout = QGridLayout()
layout.setSpacing(2)
layout.setMargin(2)

Omitted, added 4 buttons here

layout.setColumnMinimumWidth(4, 30)
layout.setColumnStretch(4, 1)

format_holder_w = QWidget()

self.format_holder = DFlowLayout(2, 3, 3)
format_holder_w.setLayout(self.format_holder)
for index in range(len(self.format)):
w = self.widget_for(index)
self.format_holder.addWidget(w)

if isinstance(self.element.value, Integer):
self.format_holder.addWidget(DElementWidget(DObjec t(RealNumber(6)*RealNumber(7), parent=self.element)))

scrollarea = QScrollArea()
scrollarea.setWidget(format_holder_w)
layout.addWidget(scrollarea, 1, 0, 1, 5)

self.setLayout(layout)

def widget_for(self, index):
return QLabel(unicode(self.format[index]))

Here are parts of the layout:



class DFlowLayout(QLayout):
'''Translated from http://doc.qt.nokia.com/latest/layouts-flowlayout.html'''
def __init__(self, margins = -1, hspace=-1, vspace=-1, *args):
super(DFlowLayout, self).__init__(*args)
self.setContentsMargins(margins, margins, margins, margins)
self._hspace = hspace
self._vspace = vspace
self.valign = Qt.AlignCenter
self.halign = Qt.AlignLeft
self.items = []

def addItem(self, item):
print 'added', item
self.items.append(item)

def replaceItem(self, old, item):
....
Omitted, not used
....

def count(self):
print len(self.items)
return len(self.items)

def horizontalSpacing(self):
if self._hspace >= 0: return self._hspace
else: return self.smartSpacing(QStyle.PM_LayoutHorizontalSpacin g)

def takeAt(self, index):
v = self.items[index]
self.items = self.items[:index] + self.items[index+1:]
return v

def itemAt(self, index):
print 'get', index
return self.items[index]

def expandingDirections(self):
return Qt.Orientation()

def setGeometry(self, rect):
QLayout.setGeometry(self, rect)
self.doLayout(rect)

def lines(self, width = -1):
...
Omitted, returns the wrapped lines and their dimensions
....
return result, widths, heights

def hasHeightForWidth(self):
return True

def heightForWidth(self, width):
return sum(self.lines(width - 2*self.margin())[2]) + 2*self.margin()

def sizeHint(self):
return self.minimumSize()

def minimumSize(self):
res = self.lines()
if len(res[0]) == 0:
return QSize()
width = max(res[1]) + 2*self.margin()
height = sum(res[2]) + 2*self.margin()
#print height
return QSize(width, height)

def doLayout(self, rect):
.....
Omitted, places lines
.....

def verticalSpacing(self):
if self._vspace >= 0: return self._vspace
else: return self.smartSpacing(QStyle.PM_LayoutVerticalSpacing)

def smartSpacing(self, thingy):
parent = self.parent()
if parent == None:
return None
elif parent.isWidgetType():
return thingy.style().pixelMetric(thingy, 0, thingy)
else:
return parent.spacing()

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?

space_otter
5th October 2011, 21:23
I replaced my layout with a QHBoxLayout and there were no errors.