Segmentation fault when move a group item (combining a rectangle and a point) in pyQT



  • originally I posted here.
    http://www.qtcentre.org/threads/68494-Segmentation-fault-when-move-a-group-item-(combining-a-rectangle-and-a-point)-in-QT

    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.ItemIgnoresTransformations)
            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()
    
    
    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 IDRectItem(RectItem):
        hotkeys = dict(
            [('i',    cycleValue(IgnorePrefix('id'), range(36)))] +
            [(str(i), cycleValue(IgnorePrefix('id'), [i])) for i in range(10)] +
            [(chr(i-10+65).lower(), cycleValue(IgnorePrefix('id'), [i])) for i in range(10, 36)]
        )
        defaultAutoTextKeys = ['id']
    
    
    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',
        },
    


  • I am suspecting when I move the BBoxFaceItem, something is corrupted.


  • Moderators

    @helloworld12345 Did you try to debug your app to at least locate the place where it crashes? It is a lot of code to analyse and to try to find out what the problem is...



  • @jsulm When the pop up Message Box shows "python has stopped working", I clicked "debug" rather than close, then visual studio was started, in visual studio,
    it shows
    Unhandled exception at 0x0000000059A17DF1 (QtGui4.dll) in python.exe: 0xC000041D: An unhandled exception was encountered during a user
    callback

    I was using pycharm to trace code step by step, however it nev, aer run to this issue by step by step since there are two many small steps/callback/signal/slot to go through, as long as I let it run without breakpoint, it will crash with segmentation fault.



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.