PDA

View Full Version : Segmentation fault when move a group item (combining a rectangle and a point) in QT



helloworld12345
14th July 2017, 01:21
There are 3 items. Rectangle, Point, BBoxFaceItem (combining rectangle and points)
There is no problem when I draw rectangle, point in 3 images, then move, resize, everything is good.
But app will crash if I group these two together as BBoxFaceItem, firstly I draw one BBoxFaceItem on image 1, then move this BBoxFaceItem, then click image 2, whole app will crash.

One messageBox pops up syaing python.exe has stopped working.
And in the console, it says segmentation fault.

Thanks a lot for your help, appreciate your comments.

Below code is just part of app.


class BaseItem(QAbstractGraphicsShapeItem):
"""
Base class for visualization items.
"""

cycleValuesOnKeypress = {}
hotkeys = {}
defaultAutoTextKeys = []

def __init__(self, model_item=None, prefix="", parent=None):
"""
Creates a visualization item.
"""
QAbstractGraphicsShapeItem.__init__(self, parent)
self.setFlags(QGraphicsItem.ItemIsSelectable |
QGraphicsItem.ItemIsMovable |
QGraphicsItem.ItemSendsGeometryChanges |
QGraphicsItem.ItemSendsScenePositionChanges)

self._model_item = model_item
if self._model_item is not None:
self._model_item.model().dataChanged.connect(self. onDataChanged)

# initialize members
self._prefix = prefix
self._auto_text_keys = self.defaultAutoTextKeys[:]
self._text = ""
self._text_bg_brush = None
self._text_item = QGraphicsTextItem(self)
self._text_item.setPos(0, 0)
self._text_item.setAcceptHoverEvents(False)
self._text_item.setFlags(QGraphicsItem.ItemIgnores Transformations)
self._text_item.setHtml(self._compile_text())
self._valid = True

if len(self.cycleValuesOnKeypress) > 0:
logging.warning("cycleValueOnKeypress is deprecated and will be removed in the future. " +
"Set BaseItem.hotkeys instead with cycleValue()")

self.changeColor()

def changeColor(self):
if self._model_item is not None:
c = self._model_item.getColor()
if c is not None:
self.setColor(c)
return
self.setColor(Qt.yellow)

def onDataChanged(self, indexFrom, indexTo):
# FIXME why is this not updated, when changed graphically via attribute box ?
#print "onDataChanged", self._model_item.index(), indexFrom, indexTo, indexFrom.parent()
if indexFrom == self._model_item.index():
self.changeColor()
#print "hit"
# self._text_item.setHtml(self._compile_text())

def modelItem(self):
"""
Returns the model item of this items.
"""
return self._model_item

def index(self):
"""
Returns the index of this item.
"""
return self._model_item.index()

def prefix(self):
"""
Returns the key prefix of the item.
"""
return self._prefix

def setPen(self, pen):
pen = QPen(pen) # convert to pen if argument is a QColor
QAbstractGraphicsShapeItem.setPen(self, pen)
self._text_item.setDefaultTextColor(pen.color())

def setText(self, text=""):
"""
Sets a text to be displayed on this item.
"""
self._text = text
self._text_item.setHtml(self._compile_text())

def text(self):
return self._text

def setTextBackgroundBrush(self, brush=None):
"""
Sets the brush to be used to fill the background region
behind the text. Set to None to not draw a background
(leave transparent).
"""
self._text_bg_brush = brush

def textBackgroundBrush(self):
"""
Returns the background brush for the text region.
"""
return self._text_bg_brush

def setAutoTextKeys(self, keys=None):
"""
Sets the keys for which the values from the annotations
are displayed automatically as text.
"""
self._auto_text_keys = keys or []
self._text_item.setHtml(self._compile_text())

def autoTextKeys(self):
"""
Returns the list of keys for which the values from
the annotations are displayed as text automatically.
"""
return self._auto_text_keys

def isValid(self):
"""
Return whether this graphics item is valid, i.e. has
a matching, valid model item connected to it. An item is
by default valid, will only be set invalid on failure.
"""
return self._valid

def setValid(self, val):
self._valid = val

def _compile_text(self):
text_lines = []
if self._text != "" and self._text is not None:
text_lines.append(self._text)
for key in self._auto_text_keys:
text_lines.append("%s: %s" % \
(key, self._model_item.get(key, "")))
return '<br/>'.join(text_lines)

def dataChanged(self):
self.dataChange()
self._text_item.setHtml(self._compile_text())
self.update()

def dataChange(self):
pass

def updateModel(self, ann=None):
if ann is not None:
self._model_item.update(ann)

def boundingRect(self):
return QRectF(0, 0, 0, 0)

def setColor(self, color):
self.setPen(color)
self.setBrush(color)
self.update()

def paint(self, painter, option, widget=None):
pass

def itemChange(self, change, value):
if change == QGraphicsItem.ItemPositionHasChanged:
self.updateModel()
return QAbstractGraphicsShapeItem.itemChange(self, change, value)

def keyPressEvent(self, event):
"""
This handles the value cycling as defined in cycleValuesOnKeypress.
"""
if str(event.text()) in self.cycleValuesOnKeypress:
itemkey, valuelist = self.cycleValuesOnKeypress[str(event.text())]
if isinstance(itemkey, IgnorePrefix):
itemkey = itemkey.value
else:
itemkey = self.prefix() + itemkey
if len(valuelist) > 0:
oldvalue = self._model_item.get(itemkey, None)
if oldvalue is None:
nextindex = 0
else:
try:
nextindex = valuelist.index(oldvalue) + 1
nextindex %= len(valuelist)
except ValueError:
nextindex = 0
newvalue = valuelist[nextindex]
if newvalue is None:
if oldvalue is not None:
self._model_item.delete(itemkey)
else:
self._model_item[itemkey] = valuelist[nextindex]
self.dataChanged()
event.accept()
elif str(event.text()) in self.hotkeys:
self.hotkeys[str(event.text())](self)
event.accept()


class PointItem(BaseItem):
"""
Visualization item for points.
"""

def __init__(self, model_item=None, prefix="", parent=None):
BaseItem.__init__(self, model_item, prefix, parent)

self._radius = 2
self._point = None
self.updatePoint()

def setRadius(self, radius):
self.prepareGeometryChange()
self._radius = radius
self.update()

def radius(self):
return self._radius

def __call__(self, model_item=None, parent=None):
pointitem = PointItem(model_item, parent)
pointitem.setPen(self.pen())
pointitem.setBrush(self.brush())
pointitem.setRadius(self._radius)
return pointitem

def dataChange(self):
self.updatePoint()

def updateModel(self):
self._model_item.update({
self.prefix() + 'x': self.scenePos().x(),
self.prefix() + 'y': self.scenePos().y(),
})

def updatePoint(self):
if self._model_item is None:
return

try:
point = QPointF(float(self._model_item[self.prefix() + 'x']),
float(self._model_item[self.prefix() + 'y']))
except KeyError as e:
LOG.debug("PointItem: Could not find expected key in item: "
+ str(e) + ". Check your config!")
self.setValid(False)
self._point = None
return

if point == self._point:
return

self.prepareGeometryChange()
self._point = point
self.setPos(self._point)

def boundingRect(self):
r = self._radius
return QRectF(-r, -r, 2 * r, 2 * r)

def paint(self, painter, option, widget=None):
BaseItem.paint(self, painter, option, widget)

pen = self.pen()
if self.isSelected():
pen.setStyle(Qt.DashLine)
painter.setPen(pen)
painter.drawEllipse(self.boundingRect())

def keyPressEvent(self, event):
BaseItem.keyPressEvent(self, event)
step = 1
if event.modifiers() & Qt.ShiftModifier:
step = 5
ds = {Qt.Key_Left: (-step, 0),
Qt.Key_Right: (step, 0),
Qt.Key_Up: (0, -step),
Qt.Key_Down: (0, step)
}.get(event.key(), None)
if ds is not None:
self.moveBy(*ds)
event.accept()



Not done yet, see next reply


class RectItem(BaseItem):
def __init__(self, model_item=None, prefix="", parent=None):
BaseItem.__init__(self, model_item, prefix, parent)

self._rect = None
self._resize = False
self._resize_start = None
self._resize_start_rect = None
self._upper_half_clicked = None
self._left_half_clicked = None

self._updateRect(self._dataToRect(self._model_item ))
LOG.debug("Constructed rect %s for model item %s" %
(self._rect, model_item))

def __call__(self, model_item=None, parent=None):
item = RectItem(model_item, parent)
item.setPen(self.pen())
item.setBrush(self.brush())
return item

def _dataToRect(self, model_item):
if model_item is None:
return QRectF()

try:
return QRectF(float(model_item[self.prefix() + 'x']),
float(model_item[self.prefix() + 'y']),
float(model_item[self.prefix() + 'width']),
float(model_item[self.prefix() + 'height']))
except KeyError as e:
LOG.debug("RectItem: Could not find expected key in item: "
+ str(e) + ". Check your config!")
self.setValid(False)
return QRectF()

def _updateRect(self, rect):
if rect == self._rect:
return

self.prepareGeometryChange()
self._rect = rect
self.setPos(rect.topLeft())

def updateModel(self):
self._rect = QRectF(self.scenePos(), self._rect.size())
self._model_item.update({
self.prefix() + 'x': float(self._rect.topLeft().x()),
self.prefix() + 'y': float(self._rect.topLeft().y()),
self.prefix() + 'width': float(self._rect.width()),
self.prefix() + 'height': float(self._rect.height()),
})

def boundingRect(self):
return QRectF(QPointF(0, 0), self._rect.size())

def paint(self, painter, option, widget=None):
BaseItem.paint(self, painter, option, widget)

pen = self.pen()
if self.isSelected():
pen.setStyle(Qt.DashLine)
painter.setPen(pen)
painter.drawRect(self.boundingRect())

def dataChange(self):
rect = self._dataToRect(self._model_item)
self._updateRect(rect)

def mousePressEvent(self, event):
#if event.modifiers() & Qt.ControlModifier != 0:
if event.button() & Qt.RightButton != 0:
self._resize = True
self._resize_start = event.scenePos()
self._resize_start_rect = QRectF(self._rect)
self._upper_half_clicked = (event.scenePos().y() < self._resize_start_rect.center().y())
self._left_half_clicked = (event.scenePos().x() < self._resize_start_rect.center().x())
event.accept()
else:
BaseItem.mousePressEvent(self, event)

def mouseMoveEvent(self, event):
if self._resize:
diff = event.scenePos() - self._resize_start
if self._left_half_clicked:
x = self._resize_start_rect.x() + diff.x()
w = self._resize_start_rect.width() - diff.x()
else:
x = self._resize_start_rect.x()
w = self._resize_start_rect.width() + diff.x()

if self._upper_half_clicked:
y = self._resize_start_rect.y() + diff.y()
h = self._resize_start_rect.height() - diff.y()
else:
y = self._resize_start_rect.y()
h = self._resize_start_rect.height() + diff.y()

rect = QRectF(QPointF(x,y), QSizeF(w, h)).normalized()

self._updateRect(rect)
self.updateModel()
event.accept()
else:
BaseItem.mouseMoveEvent(self, event)

def mouseReleaseEvent(self, event):
if self._resize:
self._resize = False
event.accept()
else:
BaseItem.mouseReleaseEvent(self, event)

def keyPressEvent(self, event):
BaseItem.keyPressEvent(self, event)
step = 1
if event.modifiers() & Qt.ShiftModifier:
step = 5
ds = {Qt.Key_Left: (-step, 0),
Qt.Key_Right: (step, 0),
Qt.Key_Up: (0, -step),
Qt.Key_Down: (0, step),
}.get(event.key(), None)
if ds is not None:
if event.modifiers() & Qt.ControlModifier:
rect = self._rect.adjusted(*((0, 0) + ds))
else:
rect = self._rect.adjusted(*(ds + ds))
self._updateRect(rect)
self.updateModel()
event.accept()

class GroupItem(BaseItem):
items = []

def __init__(self, model_item=None, prefix="", parent=None):
self._children = []
BaseItem.__init__(self, model_item, prefix, parent)
self.setFlag(QGraphicsItem.ItemIsMovable, False)

self.createChildren()

def createChildren(self):
for callable_, prefix in self.items:
child = callable_(self._model_item, prefix, self)
self._children.append(child)

def setColor(self, *args, **kwargs):
for c in self._children:
c.setColor(*args, **kwargs)
BaseItem.setColor(self, *args, **kwargs)

def boundingRect(self):
br = QRectF()
for item in self.childItems():
if item is self._text_item:
continue
br |= item.mapRectToParent(item.boundingRect())
return br


class OccludablePointItem(PointItem):
hotkeys = {
'o': cycleValue('occluded', [True, False])
}

def __init__(self, *args, **kwargs):
PointItem.__init__(self, *args, **kwargs)
self.updateColor()

def dataChange(self):
PointItem.dataChange(self)
self.updateColor()

def updateColor(self):
key = self.prefix() + 'occluded'
if key in self._model_item:
occluded = self._model_item[key]
self.setColor(Qt.red if occluded else Qt.yellow)


class BBoxFaceItem(GroupItem):
items = [
(IDRectItem, "bbox"),
(OccludablePointItem, "lec"),
(OccludablePointItem, "rec"),
(OccludablePointItem, "mc"),
]




The full code is here,
https://github.com/cvhciKIT/sloth

All the changes I make is adding below lines in sloth/conf/default_config.py
from line 73-80

{
'attributes': {
'class': 'bbx', "id" : ["0", "1"]
},
'inserter': 'sloth.items.BBoxFaceInserter',
'item': 'sloth.items.BBoxFaceItem',
'text': 'bbx',
},

Added after 1 29 minutes:

I am suspecting when I move the BBoxFaceItem, something is corrupted. I haven't figured out why.

high_flyer
14th July 2017, 12:36
Run with a debugger - and see which line is the one causing the crash.
This will give you a major hint whats wrong.

helloworld12345
14th July 2017, 18:23
Run with a debugger - and see which line is the one causing the crash.
This will give you a major hint whats wrong.

Hi high_flyer,
I am using pycharm to debug, I can't find the line that causes the crash.

Before crashing, it will reach this line below.
sys.exit(app.exec_())

Then
If I stop over(one step), it will crash.
If I step into, it will never reach to the crash line, seems it goes through many callback code, but never to the line causing crash.

high_flyer
15th July 2017, 23:34
Are you running a debug build?

helloworld12345
20th July 2017, 02:01
Are you running a debug build?

Yes, I run with debug mode, otherwise I can't run step by step.

But I figured out how to solve it.
As I stated at the beginning. I have 1 rectangle, 1 point and 1 group item(combing rectangle and point)
sole rectangle and point work good, but group item doesn't.

Each of these 3 items have their own boundingRect, after I removed the boundingRect of the group item, the crash issue is fixed.
it seems like the boundingRect in group item conflicts with the ones in sole rectangle and sole point.