"""
Base class for visualization items.
"""
cycleValuesOnKeypress = {}
hotkeys = {}
defaultAutoTextKeys = []
def __init__(self, model_item=None, prefix="", parent=None):
"""
Creates a visualization item.
"""
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.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 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):
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):
self.updateModel()
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 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()
To copy to clipboard, switch view to plain text mode
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:
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)
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):
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()
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.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):
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"),
]
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"),
]
To copy to clipboard, switch view to plain text mode
Bookmarks