class RadarGraphicsScene(QtGui.QGraphicsScene): """ Reimplemented to access the public methods to do my own thing. """ sceneDoubleClicked = Signal(QtCore.QPointF) radarItemClicked = Signal(RadarGraphicsItem) radarItemAdded = Signal(RadarGraphicsItem) radarItemMoved = Signal(RadarGraphicsItem) def __init__(self,*args, **kwargs): super(RadarGraphicsScene, self).__init__(*args, **kwargs) self.mongoSceneHandle = None self.radarItemTableModel = None self.backgroundBrush = QtGui.QBrush(QtGui.QColor.fromRgb(34, 17, 17, 50)) self.backgroundPenLines = QtGui.QPen(QtGui.QColor.fromRgb(0153, 38, 0, 25), 2) self.backgroundPenRings = QtGui.QPen(QtGui.QColor.fromRgb(153, 38, 0, 75), 2) self.backgroundPenLinesBold = QtGui.QPen(QtGui.QColor.fromRgb(153, 38, 0, 80), 2) self.setupBackground() self.setupAnimatedRadar() self.attributeEditor = None self.listPanel = None self._itemDict = {} def initScene(self, mongoSceneHandle, attribEditor, listPanel): """ Setup the scene with the data form the db, builds the table model for the views to connect to. :param mongoSceneHandle: MongoSceneHandle, RadarAttributeEditor, RadarListPanel :return: None """ self.mongoSceneHandle = mongoSceneHandle self.sourceModel = RadarItemsTableModel(mongoSceneHandle) self.proxyModel = ItemFilterProxyMode(self) self.proxyModel.setSourceModel(self.sourceModel) self.attributeEditor = attribEditor self.listPanel = listPanel self.attributeEditor.clearData() assert isinstance(self.attributeEditor, RadarAttributeEditor) assert isinstance(self.listPanel, RadarListPanel) self.listPanel.form.filter_lineEdit.textChanged.connect(self.proxyModel.setFilterRegExp) self.listPanel.radarListSelectionChanged.connect(self.selectRadarItemByID) self.proxyModel.setFilterKeyColumn(self.sourceModel.columns.index("name")) # Add all the items to the scene for i in self.mongoSceneHandle.items(): graphicsItem = RadarGraphicsItem() graphicsItem.setId(i["_id"]) graphicsItem.setPos(i["pos"][0], i["pos"][1]) graphicsItem.setColour(QtGui.QColor(*i["colour"])) graphicsItem.record = i self.addItem(graphicsItem) self.sourceModel.updateGraphicsItemColour.connect(graphicsItem.updateColour) def addItem(self, item): idx = getattr(item, 'id', '') if idx: self._itemDict[str(idx())] = item return super(RadarGraphicsScene, self).addItem(item) def removeItem(self, item): idx = getattr(item, 'id', '') if idx: self._itemDict.pop(str(idx()), None) super(RadarGraphicsScene, self).removeItem(item) def setActive(self): if self.attributeEditor: self.attributeEditor.setGraphicsScene(self) if self.listPanel: self.listPanel.setGraphicsScene(self) def radarItemToProxyIndex(self, item, columnName='name'): index = self.radarItemToSourceIndex(item, columnName) if index.isValid() and index.model() is self.sourceModel: return self.proxyModel.mapFromSource(index) def radarItemToSourceIndex(self, item, columnName='name'): rowData = self.sourceModel.rowModelIndexFromId(item.id()) if rowData: return rowData[columnName] def radarItemToSourceRowIndexData(self, item): return self.sourceModel.rowModelIndexFromId(item.id()) def updateItemRecord(self, item): record = self.sourceModel.rawDataFromId(item.id()) item.record = record def selectRadarItemByID(self, scene, idx): """ Handles selecting of radar items in the scene by the item id from mongoDB. Other views can pass the id via a signal. This slot can then handle the the selection of the items in the scene from the id :param idx: str :return: None """ # Grab the View from the current Tab and thus dig down into the scene graph. # The the method to extract the radarItem. if scene is self: log.debug("Select Radar Item by ID") item = self.itemFromID(idx) log.debug("Found GraphicsItem in Radar : {0}".format(item)) if item: # It was difficult to find out the best way to select items in the scene. This is like drawing # a box selection on the viewport and the contained items are selected. self.clearSelection() painterPath = QtGui.QPainterPath() painterPath.addRect(item.boundingRect()) painterPath.translate(item.scenePos()) self.attributeEditor.setRadarItem(item) self.setSelectionArea(painterPath, QtGui.QTransform()) def maxRadarDiameter(self): return self.width() + self.height() / 2 def setupBackground(self): """ I was so tempted to just download a nice picture but in the end I manually drew the radar on the canvas :return: None """ backgroundBrush = self.backgroundBrush backgroundPenLines = self.backgroundPenLines backgroundPenRings = self.backgroundPenRings diameter = self.maxRadarDiameter() topY = diameter / 2 * -1 topX = diameter / 2 * -1 bottomX = topX * -1 bottomY = topY * -1 ringCount = 10 ringOffset = diameter / ringCount screenPosX = (diameter / 2) * -1 screenPosY = (diameter / 2) * -1 numberOfLines = ringCount * 2 # draw the radar circles for i in range(ringCount): self.addEllipse(0-(diameter/2), 0-(diameter/2), diameter, diameter, backgroundPenRings, backgroundBrush) diameter -= ringOffset # overlay the grid for i in range(numberOfLines): if i not in [0, numberOfLines]: l = self.addLine(screenPosX, topX, screenPosX+1, bottomX, backgroundPenLines) ll = self.addLine(topX, screenPosY, bottomX, screenPosY+1, backgroundPenLines) ## Draw the darker center lines if screenPosX == 0: l.setPen(self.backgroundPenLinesBold) if screenPosY == 0: ll.setPen(self.backgroundPenLinesBold) screenPosX += ringOffset / 2 screenPosY += ringOffset / 2 def setupAnimatedRadar(self): pen = QtGui.QPen(QtGui.QColor.fromRgb(153, 38, 0, 25), 2) brush = QtGui.QBrush(QtGui.QColor.fromRgb(0, 150, 150, 25)) rad = self.maxRadarDiameter() / 2 self.radarAnimItem = self.addEllipse(0-rad, 0-rad, rad*2, rad*2, pen, brush) self.radarAnimItem.setPos(0,0) self.radarAnimItem.setSpanAngle(360 * 2) self.timeline = QtCore.QTimeLine(5000) self.timeline.setEasingCurve(QtCore.QEasingCurve.Linear) self.timeline.setLoopCount(0) self.timeline.setFrameRange(0, 1) self.animclip = QtGui.QGraphicsItemAnimation() self.animclip.setItem(self.radarAnimItem) self.animclip.setTimeLine(self.timeline) self.animclip.setPosAt(0, QtCore.QPointF(0, 0)) self.animclip.setRotationAt(1, 360) self.timeline.start() self.timeline.valueChanged.connect(self.itemAnimUpdate) def itemAnimUpdate(self, f): collidingItems = [i for i in self.collidingItems(self.radarAnimItem) if i in self._itemDict.values()] if collidingItems: i = random.choice(collidingItems) i.play() nonCollidingItems = [i for i in self._itemDict.values() if i not in collidingItems] for i in nonCollidingItems: i.stop() def showHideAnimatedRadar(self, state): item = getattr(self, "radarAnimItem", None) if item: if state: self.timeline.stop() item.hide() else: item.show() self.timeline.start() def mouseDoubleClickEvent(self, QGraphicsSceneMouseEvent): if QGraphicsSceneMouseEvent.button() == QtCore.Qt.LeftButton and \ QGraphicsSceneMouseEvent.modifiers() == QtCore.Qt.ControlModifier: self.addRadarItem(QGraphicsSceneMouseEvent.scenePos()) return super(RadarGraphicsScene, self).mouseDoubleClickEvent(QGraphicsSceneMouseEvent) def mousePressEvent(self, QGraphicsSceneMouseEvent, **kwargs): item = self.itemAt(QGraphicsSceneMouseEvent.scenePos()) if item: if getattr(item, "id", ""): item.cachePosition() self.updateItemRecord(item) self.listPanel.selectRadarItem(self, item) self.attributeEditor.setRadarItem(item) return super(RadarGraphicsScene, self).mousePressEvent(QGraphicsSceneMouseEvent) def mouseReleaseEvent(self, QGraphicsSceneMouseEvent): item = self.itemAt(QGraphicsSceneMouseEvent.scenePos()) if getattr(item, "id", ""): if item.hasMoved(): item.cachePosition() index = self.radarItemToSourceIndex(item, 'pos') log.debug("Item Moved") self.sourceModel.setData(index, [item.scenePos().x(), item.scenePos().y()], QtCore.Qt.EditRole) self.proxyModel.reset() return super(RadarGraphicsScene, self).mouseReleaseEvent(QGraphicsSceneMouseEvent) def addRadarItem(self, pos): record = self.sourceModel.addNewRadarItem() graphicsItem = RadarGraphicsItem() graphicsItem.setId(record["_id"]) self.sourceModel.updateGraphicsItemColour.connect(graphicsItem.updateColour) self.addItem(graphicsItem) graphicsItem.setPos(pos) graphicsItem.record = record self.mongoSceneHandle.updatePosition(record["_id"], pos.x(), pos.y()) self.radarItemAdded.emit(graphicsItem) def filterRadarItems(self): """ :return: """ # Probably should use layers! return [i for i in self.items() if getattr(i, "id", "")] def itemFromID(self, idx): if idx in self._itemDict: return self._itemDict[idx] log.debug("finding RadarGraphicsItem in scene by ID : {0}".format(idx)) found = [i for i in self.filterRadarItems() if i.id() == idx] if found: return found[0]