class DiscardView(QGraphicsView): """Simple extension of QGraphicsView - containing an image and click-to-zoom/unzoom """ def __init__(self, fnames): QGraphicsView.__init__(self) self.initUI(fnames) def initUI(self, fnames): # Make QGraphicsScene self.scene = QGraphicsScene() # TODO = handle different image sizes. self.images = {} self.imageGItem = QGraphicsItemGroup() self.scene.addItem(self.imageGItem) self.updateImage(fnames) self.setBackgroundBrush(QBrush(Qt.darkCyan)) def updateImage(self, fnames): """Update the image with that from filename""" for n in self.images: self.imageGItem.removeFromGroup(self.images[n]) self.images[n].setVisible(False) if fnames is not None: x = 0 n = 0 for fn in fnames: self.images[n] = QGraphicsPixmapItem(QPixmap(fn)) self.images[n].setTransformationMode(Qt.SmoothTransformation) self.images[n].setPos(x, 0) self.images[n].setVisible(True) self.scene.addItem(self.images[n]) x += self.images[n].boundingRect().width() + 10 self.imageGItem.addToGroup(self.images[n]) n += 1 # Set sensible sizes and put into the view, and fit view to the image. br = self.imageGItem.boundingRect() self.scene.setSceneRect( 0, 0, max(1000, br.width()), max(1000, br.height()), ) self.setScene(self.scene) self.fitInView(self.imageGItem, Qt.KeepAspectRatio) def mouseReleaseEvent(self, event): """Left/right click to zoom in and out""" if (event.button() == Qt.RightButton) or ( QGuiApplication.queryKeyboardModifiers() == Qt.ShiftModifier): self.scale(0.8, 0.8) else: self.scale(1.25, 1.25) self.centerOn(event.pos()) def resetView(self): """Reset the view to its reasonable initial state.""" self.fitInView(self.imageGItem, Qt.KeepAspectRatio)
class IDView(QGraphicsView): """Simple extension of QGraphicsView - containing an image and click-to-zoom/unzoom """ def __init__(self, parent, fnames): QGraphicsView.__init__(self) self.parent = parent self.initUI(fnames) def initUI(self, fnames): # Make QGraphicsScene self.scene = QGraphicsScene() # TODO = handle different image sizes. self.images = {} self.imageGItem = QGraphicsItemGroup() self.scene.addItem(self.imageGItem) self.updateImage(fnames) self.setBackgroundBrush(QBrush(Qt.darkCyan)) self.parent.tool = "zoom" self.boxFlag = False self.originPos = QPointF(0, 0) self.currentPos = self.originPos self.boxItem = QGraphicsRectItem() self.boxItem.setPen(QPen(Qt.darkCyan, 1)) self.boxItem.setBrush(QBrush(QColor(0, 255, 0, 64))) def updateImage(self, fnames): """Update the image with that from filename""" for n in self.images: self.imageGItem.removeFromGroup(self.images[n]) self.images[n].setVisible(False) if fnames is not None: x = 0 n = 0 for fn in fnames: self.images[n] = QGraphicsPixmapItem(QPixmap(fn)) self.images[n].setTransformationMode(Qt.SmoothTransformation) self.images[n].setPos(x, 0) self.images[n].setVisible(True) self.scene.addItem(self.images[n]) x += self.images[n].boundingRect().width() + 10 self.imageGItem.addToGroup(self.images[n]) n += 1 # Set sensible sizes and put into the view, and fit view to the image. br = self.imageGItem.boundingRect() self.scene.setSceneRect( 0, 0, max(1000, br.width()), max(1000, br.height()), ) self.setScene(self.scene) self.fitInView(self.imageGItem, Qt.KeepAspectRatio) def deleteRect(self): if self.boxItem.scene() is None: return self.scene.removeItem(self.boxItem) self.parent.rectangle = None def mousePressEvent(self, event): if self.parent.tool == "rect": self.originPos = self.mapToScene(event.pos()) self.currentPos = self.originPos self.boxItem.setRect(QRectF(self.originPos, self.currentPos)) if self.boxItem.scene() is None: self.scene.addItem(self.boxItem) self.boxFlag = True else: super(IDView, self).mousePressEvent(event) def mouseMoveEvent(self, event): if self.parent.tool == "rect" and self.boxFlag: self.currentPos = self.mapToScene(event.pos()) if self.boxItem is None: return else: self.boxItem.setRect(QRectF(self.originPos, self.currentPos)) else: super(IDView, self).mousePressEvent(event) def mouseReleaseEvent(self, event): if self.boxFlag: self.boxFlag = False self.parent.rectangle = self.boxItem.rect() self.parent.whichFile = self.parent.vTW.currentIndex() return """Left/right click to zoom in and out""" if (event.button() == Qt.RightButton) or ( QGuiApplication.queryKeyboardModifiers() == Qt.ShiftModifier): self.scale(0.8, 0.8) else: self.scale(1.25, 1.25) self.centerOn(event.pos()) def resetView(self): """Reset the view to its reasonable initial state.""" self.fitInView(self.imageGItem, Qt.KeepAspectRatio)
class Ui_MainWindow(QtWidgets.QMainWindow, Ui_MainWindowBase): def __init__(self): super(Ui_MainWindow, self).__init__() self.setupUi(self) self.videoPlaybackInit() self.imgInit() self.menuInit() self.df = None self.trackingPathGroup = None self.drawingFlag = False self.handInputSystem = None self.handInputSystem = HandInputSystem() self.handInputSystem.setRect(self.inputScene.sceneRect()) self.inputScene.addItem(self.handInputSystem) self.handInputSystem.addNewDataFrame() self.currentFrameNo = 0 self.colors = [] self.circleCheckBox.stateChanged.connect( self.polyLineCheckBoxStateChanged) self.lineCheckBox.stateChanged.connect( self.polyLineCheckBoxStateChanged) self.overlayCheckBox.stateChanged.connect( self.overlayCheckBoxStateChanged) self.radiusSpinBox.valueChanged.connect(self.radiusSpinBoxValueChanged) self.frameNoSpinBox.valueChanged.connect( self.frameNoSpinBoxValueChanged) self.groupBox_2.hide() self.inputGraphicsView.viewport().setCursor(QtCore.Qt.ArrowCursor) # self.optionViewButton.pressed.connect(self.optionViewButtonPressed) self.zoomedGraphicsView.hide() self.dataFrameWidget.dataFrameChanged.connect(self.dataFrameChanged) self.dataFrameWidget.hide() self.handInputSystem.setColor(self.dataFrameWidget.getColor()) #self.processDropedFile("/Users/ymnk/temp/Dast/2016/01/hoge.avi") #self.processDropedFile("./a.csv") def dataFrameChanged(self, addedFrameFlag, editingNo, color): if addedFrameFlag: self.handInputSystem.addNewDataFrame() self.handInputSystem.setEditingNo(editingNo) self.handInputSystem.setColor(color) print(addedFrameFlag, color, editingNo) def optionViewButtonPressed(self): if self.groupBox_2.isVisible(): self.optionViewButton.setText("<") self.groupBox_2.hide() else: self.optionViewButton.setText(">") self.groupBox_2.show() def overlayCheckBoxStateChanged(self, s): if self.overlayCheckBox.isChecked(): self.frameBufferItemGroup.show() else: self.frameBufferItemGroup.hide() self.updateInputGraphicsView() def polyLineCheckBoxStateChanged(self, s): overlayFrameNo = self.frameNoSpinBox.value() min_value = max(self.currentFrameNo - overlayFrameNo, 0) current_pos = self.currentFrameNo - min_value if self.handInputSystem is not None: self.handInputSystem.setDrawItem(current_pos, self.circleCheckBox.isChecked()) self.handInputSystem.setDrawLine(self.lineCheckBox.isChecked()) self.updateInputGraphicsView() def radiusSpinBoxValueChanged(self, value): if self.handInputSystem is not None: self.handInputSystem.setRadius(self.radiusSpinBox.value()) self.updateInputGraphicsView() def frameNoSpinBoxValueChanged(self, value): if self.handInputSystem is not None: self.handInputSystem.setOverlayFrameNo(self.frameNoSpinBox.value()) self.updateInputGraphicsView() def dragEnterEvent(self, event): event.acceptProposedAction() def dropEvent(self, event): # event.setDropAction(QtCore.Qt.MoveAction) mime = event.mimeData() if mime.hasUrls(): urls = mime.urls() if len(urls) > 0: self.processDropedFile(urls[0].toLocalFile()) event.acceptProposedAction() def processDropedFile(self, filePath): root, ext = os.path.splitext(filePath) if ext == ".filter": # Read Filter self.openFilterFile(filePath=filePath) return elif ext == ".csv": self.openCSVFile(filePath=filePath) elif self.openImageFile(filePath=filePath): return elif self.openVideoFile(filePath=filePath): return def videoPlaybackInit(self): self.videoPlaybackWidget.hide() self.videoPlaybackWidget.frameChanged.connect(self.setFrame, Qt.QueuedConnection) def setFrame(self, frame, frameNo): if frame is not None: self.cv_img = frame self.currentFrameNo = frameNo self.updateInputGraphicsView() if self.videoInitialFlag == True: self.graphicsViewResized() self.videoInitialFlag = False self.evaluate() def imgInit(self): self.cv_img = cv2.imread( os.path.join(sampleDataPath, "color_filter_test.png")) self.frameBuffer = Queue() self.frameBufferItemGroup = QGraphicsItemGroup() self.frameBufferItemGroup.hide() self.inputPixmapRenderScene = QGraphicsScene() self.inputPixmapRenderScene.addItem(self.frameBufferItemGroup) self.inputScene = QGraphicsScene() self.inputGraphicsView.setScene(self.inputScene) self.inputGraphicsView.resizeEvent = self.graphicsViewResized # self.inputScene.addItem(self.frameBufferItemGroup) qimg = misc.cvMatToQImage(self.cv_img) self.inputPixmap = QPixmap.fromImage(qimg) self.inputPixmapItem = QGraphicsPixmapItem(self.inputPixmap) self.inputScene.addItem(self.inputPixmapItem) self.inputGraphicsView.mousePressEvent = self.inputGraphicsViewMousePressEvent self.inputGraphicsView.mouseMoveEvent = self.inputGraphicsViewMouseMoveEvent self.inputGraphicsView.mouseReleaseEvent = self.inputGraphicsViewMouseReleaseEvent self.inputGraphicsView.keyPressEvent = self.inputGraphicsViewKeyPressEvent self.inputGraphicsView.keyReleaseEvent = self.inputGraphicsViewKeyReleaseEvent self.inputGraphicsView.wheelEvent = self.inputGraphicsViewwheelEvent #self.inputGraphicsView.focusInEvent = self.inputGraphicsViewfocusInEvent self.inputGraphicsView.viewport().installEventFilter(self) self.inputGraphicsView.setMouseTracking(True) self.overlayScene = QGraphicsScene() self.inputGraphicsView.setOverlayScene(self.overlayScene) def inputGraphicsViewfocusInEvent(self, event): # QGraphicsView.focusInEvent(self.inputGraphicsView, event) def inputGraphicsViewMousePressEvent(self, event): if event.modifiers() == QtCore.Qt.ShiftModifier: # Comment out to permit the view for sending the event to the child scene. QGraphicsView.mousePressEvent(self.inputGraphicsView, event) else: self.drawingFlag = True if not self.videoPlaybackWidget.isPlaying(): self.videoPlaybackWidget.playButtonClicked() def inputGraphicsViewMouseMoveEvent(self, event): if event.modifiers() == QtCore.Qt.ShiftModifier: # Comment out to permit the view for sending the event to the child scene. QGraphicsView.mouseMoveEvent(self.inputGraphicsView, event) elif self.drawingFlag == True: pass #self.videoPlaybackWidget.moveNextButtonClicked() #self.handInputSystem.inputMouseMoveEvent(mousePosition,self.currentFrameNo) #print(self.currentFrameNo) #self.positionStack.append(mousePosition) #self.handInputSystem.inputMouseMoveEvent(mousePosition) def inputGraphicsViewMouseReleaseEvent(self, event): if self.drawingFlag == True: self.drawingFlag = False self.videoPlaybackWidget.playButtonClicked() self.handInputSystem.inputMouseReleaseEvent() self.handInputSystem.setPoints() # Comment out to permit the view for sending the event to the child scene. QGraphicsView.mouseReleaseEvent(self.inputGraphicsView, event) self.inputGraphicsView.viewport().setCursor(QtCore.Qt.ArrowCursor) def inputGraphicsViewKeyPressEvent(self, event): mousePosition = QCursor().pos() mousePosition = self.inputGraphicsView.mapFromGlobal(mousePosition) if event.type() == QtCore.QEvent.KeyPress: key = event.key() if key == QtCore.Qt.Key_Space: self.videoPlaybackWidget.playButtonClicked() elif key == QtCore.Qt.Key_A: self.videoPlaybackWidget.movePrevButtonClicked() elif key == QtCore.Qt.Key_D: self.videoPlaybackWidget.moveNextButtonClicked() elif key == QtCore.Qt.Key_Down: self.inputGraphicsViewScaleDown() elif key == QtCore.Qt.Key_Up: self.inputGraphicsViewScaleUp() pass elif key == QtCore.Qt.Key_R: self.graphicsViewResized() elif key == QtCore.Qt.Key_P: pass #self.handInputSystem.nextDataFrame() elif key == QtCore.Qt.Key_O: pass #self.handInputSystem.previousDataFrame() elif key == QtCore.Qt.Key_J: frameNo = self.handInputSystem.getLastInputedFrameIndex() self.videoPlaybackWidget.moveToFrame(frameNo) elif key == QtCore.Qt.Key_S: self.handInputSystem.saveCSV("./a.csv") QGraphicsView.keyPressEvent(self.inputGraphicsView, event) def inputGraphicsViewKeyReleaseEvent(self, event): QGraphicsView.keyReleaseEvent(self.inputGraphicsView, event) def inputGraphicsViewwheelEvent(self, event): scaleFactor = 1.15 if event.delta() > 0: # Zoom in self.inputGraphicsView.scale(scaleFactor, scaleFactor) else: # Zooming out self.inputGraphicsView.scale(1.0 / scaleFactor, 1.0 / scaleFactor) QGraphicsView.wheelEvent(self.inputGraphicsView, event) def inputGraphicsViewScaleDown(self): scaleFactor = 1.15 self.inputGraphicsView.scale(1.0 / scaleFactor, 1.0 / scaleFactor) def inputGraphicsViewScaleUp(self): scaleFactor = 1.15 self.inputGraphicsView.scale(scaleFactor, scaleFactor) def menuInit(self): self.actionSaveCSVFile.triggered.connect(self.saveCSVFile) self.actionOpenCSVFile.triggered.connect(self.openCSVFile) def openVideoFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Video File', userDir) if len(filePath) is not 0: self.filePath = filePath ret = self.videoPlaybackWidget.openVideo(filePath) if ret == False: return False self.videoInitialFlag = True self.videoPlaybackWidget.show() self.dataFrameWidget.show() # self.evaluate() return True else: return False def openImageFile(self, activated=False, filePath=None): if filePath == None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Image File', userDir) if len(filePath) is not 0: self.filePath = filePath img = cv2.imread(filePath) if img is None: return False self.cv_img = img self.videoPlaybackWidget.hide() self.updateInputGraphicsView() self.evaluate() return True else: return False def openCSVFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open CSV File', userDir, 'CSV files (*.csv)') if len(filePath) is not 0: self.df = pd.read_csv(filePath, index_col=0) if self.handInputSystem is not None: self.inputScene.removeItem(self.handInputSystem) self.handInputSystem = HandInputSystem() self.handInputSystem.setRect(self.inputScene.sceneRect()) self.inputScene.addItem(self.handInputSystem) self.handInputSystem.setDataFrame(self.df) self.handInputSystem.setPoints() self.dataFrameWidget.clear() self.dataFrameWidget.dataFrameNo = self.handInputSystem.dataFrameNo self.dataFrameWidget.editingNo = 0 for item in range(self.handInputSystem.dataFrameNo + 1): color = self.handInputSystem.itemList[item].getColor() print(item, color) self.dataFrameWidget.colorList.append(color) self.dataFrameWidget.setUniqueIDLabel() self.evaluate() def saveCSVFile(self, activated=False, filePath=None): #if self.df is not None: if self.handInputSystem.isDataFrame(): filePath, _ = QFileDialog.getSaveFileName(None, 'Save CSV File', userDir, "CSV files (*.csv)") if len(filePath) is not 0: logger.debug("Saving CSV file: {0}".format(filePath)) self.handInputSystem.saveCSV(filePath) def updateInputGraphicsView(self): # print("update") # self.inputScene.clear() self.inputScene.removeItem(self.inputPixmapItem) qimg = misc.cvMatToQImage(self.cv_img) self.inputPixmap = QPixmap.fromImage(qimg) p = QPainter(self.inputPixmap) sourceRect = self.inputPixmapRenderScene.sceneRect() self.inputPixmapRenderScene.render(p, QRectF(sourceRect), QRectF(sourceRect), QtCore.Qt.IgnoreAspectRatio) self.inputPixmapItem = QGraphicsPixmapItem(self.inputPixmap) rect = QtCore.QRectF(self.inputPixmap.rect()) self.inputScene.setSceneRect(rect) self.inputScene.addItem(self.inputPixmapItem) self.inputGraphicsView.viewport().update() # self.graphicsViewResized() def eventFilter(self, obj, event): if obj is self.inputGraphicsView.viewport() and event.type( ) == QEvent.Wheel: return True else: return False def graphicsViewResized(self, event=None): # print("resize") # print(self.inputScene) self.inputGraphicsView.fitInView( QtCore.QRectF(self.inputPixmap.rect()), QtCore.Qt.KeepAspectRatio) def evaluate(self): if not self.videoPlaybackWidget.isOpened(): return qimg = misc.cvMatToQImage(self.cv_img) pixmapItem = QGraphicsPixmapItem(QPixmap.fromImage(qimg)) pixmapItem.setOpacity(0.2) self.frameBuffer.put(pixmapItem) self.frameBufferItemGroup.addToGroup(pixmapItem) if self.frameBuffer.qsize() > 10: item = self.frameBuffer.get() self.frameBufferItemGroup.removeFromGroup(item) """ if self.trackingPathGroup is not None: self.trackingPathGroup.setPoints(self.currentFrameNo) """ if self.handInputSystem is not None: self.handInputSystem.setPoints(self.currentFrameNo) if self.drawingFlag is True: mousePosition = QCursor().pos() mousePosition = self.inputGraphicsView.mapFromGlobal( mousePosition) mousePosition = self.inputGraphicsView.mapToScene( mousePosition) pos = [mousePosition.x(), mousePosition.y()] self.handInputSystem.appendPosition(pos, self.currentFrameNo)
class ExamView(QGraphicsView): """Simple extension of QGraphicsView - containing an image and click-to-zoom/unzoom """ def __init__(self, fnames): QGraphicsView.__init__(self) self.initUI(fnames) def initUI(self, fnames): # set background self.setStyleSheet("background: transparent") self.setBackgroundBrush(BackGrid()) self.setRenderHint(QPainter.Antialiasing, True) self.setRenderHint(QPainter.SmoothPixmapTransform, True) # Make QGraphicsScene self.scene = QGraphicsScene() # TODO = handle different image sizes. self.images = {} self.imageGItem = QGraphicsItemGroup() self.scene.addItem(self.imageGItem) self.updateImage(fnames) def updateImage(self, fnames): """Update the image with that from filename""" for n in self.images: self.imageGItem.removeFromGroup(self.images[n]) self.images[n].setVisible(False) if fnames is not None: x = 0 for (n, fn) in enumerate(fnames): pix = QPixmap(fn) self.images[n] = QGraphicsPixmapItem(pix) self.images[n].setTransformationMode(Qt.SmoothTransformation) self.images[n].setPos(x, 0) self.images[n].setVisible(True) sf = float(ScenePixelHeight) / float(pix.height()) self.images[n].setScale(sf) self.scene.addItem(self.images[n]) # x += self.images[n].boundingRect().width() + 10 # TODO: why did this have + 10 but the scene did not? x += sf * (pix.width() - 1.0) # TODO: don't floor here if units of scene are large! x = int(x) self.imageGItem.addToGroup(self.images[n]) # Set sensible sizes and put into the view, and fit view to the image. br = self.imageGItem.boundingRect() self.scene.setSceneRect( 0, 0, max(1000, br.width()), max(1000, br.height()), ) self.setScene(self.scene) self.fitInView(self.imageGItem, Qt.KeepAspectRatio) def mouseReleaseEvent(self, event): """Left/right click to zoom in and out""" if (event.button() == Qt.RightButton) or ( QGuiApplication.queryKeyboardModifiers() == Qt.ShiftModifier): self.scale(0.8, 0.8) else: self.scale(1.25, 1.25) self.centerOn(event.pos()) def resetView(self): """Reset the view to its reasonable initial state.""" self.fitInView(self.imageGItem, Qt.KeepAspectRatio)
class Ui_MainWindow(QtWidgets.QMainWindow, Ui_MainWindowBase): def __init__(self): super(Ui_MainWindow, self).__init__() self.setupUi(self) self.videoPlaybackInit() self.imgInit() self.menuInit() self.df = {} self.trackingPathGroup = None self.movableArrowGroup = None self.line_data_dict = {} self.line_item_dict = {} self.file_name_dict = {} self.currentFrameNo = 0 self.colors = None self.overlayCheckBox.stateChanged.connect(self.overlayCheckBoxStateChanged) self.radiusSpinBox.valueChanged.connect(self.radiusSpinBoxValueChanged) self.frameNoSpinBox.valueChanged.connect(self.frameNoSpinBoxValueChanged) self.markDeltaSpinBox.valueChanged.connect(self.markDeltaSpinBoxValueChanged) def overlayCheckBoxStateChanged(self, s): if self.overlayCheckBox.isChecked(): self.frameBufferItemGroup.show() else: self.frameBufferItemGroup.hide() self.updateInputGraphicsView() def markDeltaSpinBoxValueChanged(self, value): if self.trackingPathGroup is not None: self.trackingPathGroup.setMarkDelta(self.markDeltaSpinBox.value()) self.updateInputGraphicsView() def radiusSpinBoxValueChanged(self, value): if self.trackingPathGroup is not None: self.trackingPathGroup.setRadius(self.radiusSpinBox.value()) self.updateInputGraphicsView() def frameNoSpinBoxValueChanged(self, value): if self.trackingPathGroup is not None: self.trackingPathGroup.setOverlayFrameNo(self.frameNoSpinBox.value()) self.updateInputGraphicsView() def dragEnterEvent(self,event): event.acceptProposedAction() def dropEvent(self,event): # event.setDropAction(QtCore.Qt.MoveAction) mime = event.mimeData() if mime.hasUrls(): urls = mime.urls() if len(urls) > 0: self.processDropedFile(urls[0].toLocalFile()) event.acceptProposedAction() def processDropedFile(self,filePath): root,ext = os.path.splitext(filePath) if ext == ".filter": # Read Filter self.openFilterFile(filePath=filePath) return elif ext == ".csv": self.openCSVFile(filePath=filePath) elif ext == ".json": self.openJSONFile(filePath=filePath) elif ext == ".color": self.openColorFile(filePath=filePath) elif self.openImageFile(filePath=filePath): return elif self.openVideoFile(filePath=filePath): return def videoPlaybackInit(self): self.videoPlaybackWidget.hide() self.videoPlaybackWidget.frameChanged.connect(self.setFrame, Qt.QueuedConnection) def setFrame(self, frame, frameNo): if frame is not None: self.cv_img = frame self.currentFrameNo = frameNo self.updateInputGraphicsView() self.evaluate() def imgInit(self): self.cv_img = cv2.imread(os.path.join(sampleDataPath,"color_filter_test.png")) self.frameBuffer = Queue() self.frameBufferItemGroup = QGraphicsItemGroup() self.frameBufferItemGroup.hide() self.inputPixmapRenderScene = QGraphicsScene() self.inputPixmapRenderScene.addItem(self.frameBufferItemGroup) self.inputScene = QGraphicsScene() self.inputGraphicsView.setScene(self.inputScene) self.inputGraphicsView.resizeEvent = self.graphicsViewResized # self.inputScene.addItem(self.frameBufferItemGroup) qimg = misc.cvMatToQImage(self.cv_img) self.inputPixmap = QPixmap.fromImage(qimg) self.inputPixmapItem = QGraphicsPixmapItem(self.inputPixmap) self.inputScene.addItem(self.inputPixmapItem) self.rubberBand = QtWidgets.QRubberBand(QtWidgets.QRubberBand.Rectangle, self.inputGraphicsView) self.inputGraphicsView.mousePressEvent = self.inputGraphicsViewMousePressEvent self.inputGraphicsView.mouseMoveEvent = self.inputGraphicsViewMouseMoveEvent self.inputGraphicsView.mouseReleaseEvent = self.inputGraphicsViewMouseReleaseEvent self.inputGraphicsView.viewport().installEventFilter(self) self.inputGraphicsView.setMouseTracking(True) self.overlayScene = QGraphicsScene() self.inputGraphicsView.setOverlayScene(self.overlayScene) self.zoomedGraphicsView.setScene(self.inputScene) self.zoomedGraphicsView.setOverlayScene(self.overlayScene) def inputGraphicsViewMousePressEvent(self, event): self.origin = QPoint(event.pos()) self.rubberBand.setGeometry( QtCore.QRect(self.origin, QtCore.QSize())) self.rubberBand.show() # Comment out to permit the view for sending the event to the child scene. # QGraphicsView.mousePressEvent(self.inputGraphicsView, event) def inputGraphicsViewMouseMoveEvent(self, event): if self.rubberBand.isVisible(): self.rubberBand.setGeometry( QtCore.QRect(self.origin, event.pos()).normalized()) # Comment out to permit the view for sending the event to the child scene. # QGraphicsView.mouseMoveEvent(self.inputGraphicsView, event) def inputGraphicsViewMouseReleaseEvent(self, event): if self.rubberBand.isVisible(): self.rubberBand.hide() rect = self.rubberBand.geometry() sceneRect = self.inputGraphicsView.mapToScene(rect).boundingRect() self.zoomedGraphicsView.fitInView(QRectF(sceneRect)) self.zoomedGraphicsView.viewport().update() # Comment out to permit the view for sending the event to the child scene. self.inputGraphicsView.viewport().update() # QGraphicsView.mouseReleaseEvent(self.inputGraphicsView, event) def menuInit(self): self.actionSaveDataFiles.triggered.connect(self.saveDataFiles) self.actionOpenCSVFile.triggered.connect(self.openCSVFile) self.actionOpenCSVFile.triggered.connect(self.openJSONFile) self.actionOpenCSVFile.triggered.connect(self.openJSONFile) self.actionPath.triggered.connect(self.actionPathTriggered) self.actionCircle.triggered.connect(self.actionCircleTriggered) self.actionIntervalMark.triggered.connect(self.actionIntervalMarkTriggered) self.actionShape.triggered.connect(self.actionShapeTriggered) self.actionSkeleton.triggered.connect(self.actionSkeletonTriggered) self.actionArrow.triggered.connect(self.actionArrowTriggered) self.actionChangeOrderOfNum.triggered.connect(self.actionChangeOrderOfNumTriggered) self.actionTrackingPathColor.triggered.connect(self.openTrackingPathColorSelectorDialog) def actionPathTriggered(self, checked): if self.trackingPathGroup is not None: self.trackingPathGroup.setDrawLine(checked) if not checked or self.actionIntervalMark.isChecked(): self.trackingPathGroup.setDrawMarkItem(checked) self.updateInputGraphicsView() def actionCircleTriggered(self, checked): if self.trackingPathGroup is not None: self.trackingPathGroup.setDrawItem(checked) self.updateInputGraphicsView() def actionIntervalMarkTriggered(self, checked): if self.trackingPathGroup is not None: self.trackingPathGroup.setDrawMarkItem(checked) self.updateInputGraphicsView() def actionShapeTriggered(self, checked): if 'shape' in self.line_item_dict.keys(): line_item = self.line_item_dict['shape'] if checked: line_item.show() else: line_item.hide() def actionSkeletonTriggered(self, checked): if 'skeleton' in self.line_item_dict.keys(): line_item = self.line_item_dict['skeleton'] if checked: line_item.show() else: line_item.hide() def actionArrowTriggered(self, checked): if self.movableArrowGroup is not None: if checked: self.movableArrowGroup.show() else: self.movableArrowGroup.hide() def actionChangeOrderOfNumTriggered(self, checked): if len(self.df.keys())!=0 or len(self.line_data_dict.keys())!=0: self.videoPlaybackWidget.stop() dialog = DataSwapDialog(self) dialog.setWindowModality(Qt.WindowModal) dialog.setData(self.df, self.line_data_dict) dialog.swapAccepted.connect(self.evaluate) res = dialog.exec() def openTrackingPathColorSelectorDialog(self, activated=False): if self.trackingPathGroup is not None: self.trackingPathGroup.openColorSelectorDialog(self) def openVideoFile(self, activated=False, filePath = None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Video File', userDir) if len(filePath) is not 0: self.filePath = filePath ret = self.videoPlaybackWidget.openVideo(filePath) if ret == False: return False self.videoPlaybackWidget.show() self.cv_img = self.videoPlaybackWidget.getCurrentFrame() self.initialize() return True else: return False def openImageFile(self, activated=False, filePath = None): if filePath == None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Image File', userDir) if len(filePath) is not 0: self.filePath = filePath img = cv2.imread(filePath) if img is None: return False self.cv_img = img self.videoPlaybackWidget.hide() self.updateInputGraphicsView() self.evaluate() return True else: return False def openCSVFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open CSV File', userDir, 'CSV files (*.csv)') if len(filePath) is not 0: df = pd.read_csv(filePath, index_col=0) name = df.index.name if name is None: name = 'position' self.df[name] = df self.file_name_dict[name] = filePath if name is None or name=='position': if self.trackingPathGroup is not None: self.inputScene.removeItem(self.trackingPathGroup) self.trackingPathGroup = TrackingPathGroup() self.trackingPathGroup.setRect(self.inputScene.sceneRect()) self.inputScene.addItem(self.trackingPathGroup) if not self.actionPath.isChecked(): self.trackingPathGroup.setDrawLine(False) if not self.actionCircle.isChecked(): self.trackingPathGroup.setDrawItem(False) if not self.actionIntervalMark.isChecked(): self.trackingPathGroup.setDrawMarkItem(False) self.trackingPathGroup.setDataFrame(self.df['position']) elif name=='arrow': if self.movableArrowGroup is not None: self.inputScene.removeItem(self.movableArrowGroup) self.movableArrowGroup = MovableArrowGroup() self.inputScene.addItem(self.movableArrowGroup) self.movableArrowGroup.edited.connect(self.arrowEdited) if not self.actionArrow.isChecked(): self.movableArrowGroup.hide() if 'arrow' in self.df.keys() and 'position' in self.df.keys(): self.movableArrowGroup.setDataFrame(self.df['arrow'], self.df['position']) self.initialize() def openColorFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Color File', userDir, 'Color files (*.color)') if len(filePath) is not 0: self.colors = pd.read_csv(filePath, index_col=0).as_matrix().tolist() self.colors = [QColor(*rgb) for rgb in self.colors] self.setColorsToGraphicsObjects() def openJSONFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open JSON File', userDir, 'JSON files (*.json)') if len(filePath) is not 0: with open(filePath) as f_p: data = json.load(f_p) name = data['name'] self.line_data_dict[name] = data self.file_name_dict[name] = filePath if name in self.line_item_dict.keys(): self.inputScene.removeItem(self.line_item_dict[name]) lines = MovableLineGroup() lines.setData(data) lines.setRect(self.inputScene.sceneRect()) if name=='shape' and not self.actionShape.isChecked(): lines.hide() if name=='skeleton' and not self.actionSkeleton.isChecked(): lines.hide() self.line_item_dict[name] = lines self.inputScene.addItem(lines) self.initialize() def saveDataFiles(self, activated=False, filePath = None): if len(self.df.keys())!=0: for k, v in self.df.items(): f_name, f_ext = os.path.splitext(self.file_name_dict[k]) candidate_file_path = '{0}-fixed{1}'.format(f_name, f_ext) filePath, _ = QFileDialog.getSaveFileName(None, 'Save CSV File', candidate_file_path, "CSV files (*.csv)") if len(filePath) is not 0: logger.debug("Saving CSV file: {0}".format(filePath)) df = v.copy() col_n = df.as_matrix().shape[1]/2 col_names = np.array([('x{0}'.format(i), 'y{0}'.format(i)) for i in range(int(round(col_n)))]).flatten() df.columns = pd.Index(col_names) df.to_csv(filePath) for k, v in self.line_data_dict.items(): f_name, f_ext = os.path.splitext(self.file_name_dict[k]) candidate_file_path = '{0}-fixed{1}'.format(f_name, f_ext) filePath, _ = QFileDialog.getSaveFileName(None, 'Save JSON File', candidate_file_path, "JSON files (*.json)") if len(filePath) is not 0: logger.debug("Saving JSON file: {0}".format(filePath)) with open(filePath, 'w') as f_p: json.dump(v, f_p) def updateInputGraphicsView(self): self.inputScene.removeItem(self.inputPixmapItem) qimg = misc.cvMatToQImage(self.cv_img) self.inputPixmap = QPixmap.fromImage(qimg) p = QPainter(self.inputPixmap) sourceRect = self.inputPixmapRenderScene.sceneRect() self.inputPixmapRenderScene.render(p, QRectF(sourceRect), QRectF(sourceRect), QtCore.Qt.IgnoreAspectRatio) self.inputPixmapItem = QGraphicsPixmapItem(self.inputPixmap) rect = QtCore.QRectF(self.inputPixmap.rect()) self.inputScene.setSceneRect(rect) self.inputScene.addItem(self.inputPixmapItem) self.inputGraphicsView.viewport().update() self.graphicsViewResized() def eventFilter(self, obj, event): if obj is self.inputGraphicsView.viewport() and event.type()==QEvent.Wheel: return True if event.type() == QEvent.KeyPress: if Qt.Key_Home <= event.key() <= Qt.Key_PageDown: self.videoPlaybackWidget.playbackSlider.keyPressEvent(event) return True return False def graphicsViewResized(self, event=None): self.inputGraphicsView.fitInView(QtCore.QRectF(self.inputPixmap.rect()), QtCore.Qt.KeepAspectRatio) def setColorsToGraphicsObjects(self): # FIXME: データセットと色リストのサイズ整合性チェックが必要 if self.colors is not None: if self.trackingPathGroup is not None: self.trackingPathGroup.setColors(self.colors) for k, v in self.line_item_dict.items(): v.setColors(self.colors) def initialize(self): if not self.videoPlaybackWidget.isOpened(): return if self.trackingPathGroup is not None: r = self.trackingPathGroup.autoAdjustRadius(self.cv_img.shape) self.radiusSpinBox.setValue(r) self.trackingPathGroup.autoAdjustLineWidth(self.cv_img.shape) self.trackingPathGroup.setItemsAreMovable(True) if self.movableArrowGroup is not None: pass for k, v in self.line_item_dict.items(): v.autoAdjustLineWidth(self.cv_img.shape) v.autoAdjustMarkSize(self.cv_img.shape) self.setColorsToGraphicsObjects() self.evaluate() def evaluate(self): if not self.videoPlaybackWidget.isOpened(): return qimg = misc.cvMatToQImage(self.cv_img) pixmapItem = QGraphicsPixmapItem(QPixmap.fromImage(qimg)) pixmapItem.setOpacity(0.2) self.frameBuffer.put(pixmapItem) self.frameBufferItemGroup.addToGroup(pixmapItem) if self.frameBuffer.qsize() > 10: item = self.frameBuffer.get() self.frameBufferItemGroup.removeFromGroup(item) if self.trackingPathGroup is not None: self.trackingPathGroup.setPoints(self.currentFrameNo) if self.movableArrowGroup is not None: self.movableArrowGroup.setPositions(self.currentFrameNo) for k, v in self.line_item_dict.items(): v.setPolyline(self.currentFrameNo) @pyqtSlot(object) def arrowEdited(self, name): # TODO: 方向の再推定機能の実装 # quit_msg = "Arrow {} edited.\nRe-estimate the direction in following frames?".format(name) # reply = QtWidgets.QMessageBox.question( # self, # 'Question', # quit_msg, # QtWidgets.QMessageBox.Yes, # QtWidgets.QMessageBox.No # ) # # if reply == QtWidgets.QMessageBox.Yes: # pass # else: # pass pass
class Image_widget(QWidget): param_user_modified = pyqtSignal(object) # (px1, py1, px2, px2) # zoom_params = ["x", "y", "dx", "xy_ratio"] def __init__(self, parent, view_presenter): # im=None):#, xy_ratio=None): super().__init__(parent) # self.setWindowFlags(Qt.BypassGraphicsProxyWidget) self._model = view_presenter._model self._mapping = view_presenter._mapping self._presenter = view_presenter# model[func_keys] # if xy_ratio is None: # self._im = parent._im # else: # self._im = im # sets graphics scene and view self._scene = QGraphicsScene() self._group = QGraphicsItemGroup() self._view = QGraphicsView() self._scene.addItem(self._group) self._view.setScene(self._scene) self._view.setFrameStyle(QFrame.Box) # # always scrollbars # self._view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) # self._view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) # special sursor self._view.setCursor(QtGui.QCursor(Qt.CrossCursor)) # sets property widget self._labels = QDict_viewer(self, {"Image metadata": None, "px": None, "py": None, "zoom": None}) # sets layout self._layout = QVBoxLayout(self) self.setLayout(self._layout) self._layout.addWidget(self._view, stretch=1) #self._layout.addStretch(1) self._layout.addWidget(self._labels, stretch=0) # Zoom rectangle disabled self._rect = None self._drawing_rect = False self._dragging_rect = False # Sets Image self._qim = None self.reset_im() # zooms anchors for wheel events - note this is only active # when the self._view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self._view.setResizeAnchor(QGraphicsView.AnchorUnderMouse) self._view.setAlignment(Qt.AlignCenter) # events filters self._view.viewport().installEventFilter(self) self._scene.installEventFilter(self) # Publish / subscribe signals with the submodel # self.zoom_user_modified.connect(self._model.) self._model.model_event.connect(self.model_event_slot) # self._view.setContextMenuPolicy(Qt.ActionsContextMenu) # self._scene.customContextMenuRequested.connect(self.useless) # useless_action = QAction("DoNothing", self) # self._scene.addAction(useless_action) # useless_action.triggered.connect(self.useless) def on_context_menu(self, event): menu = QMenu(self) NoAction = QAction("Does nothing", self) menu.addAction(NoAction) NoAction.triggered.connect(self.doesnothing) menu.popup(self._view.viewport().mapToGlobal(event.pos())) return True def doesnothing(self, event): print("voili voilou") @property def zoom(self): view = self._view pc = 100. * math.sqrt(view.transform().determinant()) return "{0:.2f} %".format(pc) @property def xy_ratio(self): return self._presenter["xy_ratio"] # return self.parent().xy_ratio def reset_im(self): image_file = os.path.join((self._presenter["fractal"]).directory, self._presenter["image"] + ".png") valid_image = True try: with PIL.Image.open(image_file) as im: info = im.info nx, ny = im.size # print("info debug", info["debug"]) except FileNotFoundError: valid_image = False info = {"x": None, "y": None, "dx": None, "xy_ratio": None} nx = None ny = None # Storing the "initial" zoom info self._fractal_zoom_init = {k: info[k] for k in ["x", "y", "dx", "xy_ratio"]} self._fractal_zoom_init["nx"] = nx self._fractal_zoom_init["ny"] = ny self.validate() # if self._qim is not None: # self._group.removeFromGroup(self._qim) for item in [self._qim, self._rect]: if item is not None: self._group.removeFromGroup(item) if valid_image: self._qim = QGraphicsPixmapItem(QtGui.QPixmap.fromImage( QtGui.QImage(image_file)))#QtGui.QImage()))#imqt)) # QtGui.QImage(self._im))) self._qim.setAcceptHoverEvents(True) self._group.addToGroup(self._qim) self.fit_image() else: self._qim = None self._rect = None self._drawing_rect = False @staticmethod def cast(val, example): """ Casts value to the same type as example """ return type(example)(val) def check_zoom_init(self): """ Checks if the image 'zoom init' matches the parameters ; otherwise, updates """ ret = 0 for key in ["x", "y", "dx", "xy_ratio"]:#, "dps"]: # TODO : or precision ?? expected = self._presenter[key] value = self._fractal_zoom_init[key] if value is None: ret = 2 else: casted = self.cast(value, expected) # Send a model modification request self._presenter[key] = casted if casted != str(expected) and (ret != 2): ret = 1 self._fractal_zoom_init[key] = casted return ret def validate(self): """ Sets Image metadata message """ self.validated = self.check_zoom_init() message = {0: "OK, matching", 1: "OK, zoom params updated", 2: "No image found"} self._labels.values_update({"Image metadata": message[self.validated]}) def fit_image(self): if self._qim is None: return rect = QtCore.QRectF(self._qim.pixmap().rect()) if not rect.isNull(): # # always scrollbars off self._view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) view = self._view view.setSceneRect(rect) unity = view.transform().mapRect(QtCore.QRectF(0, 0, 1, 1)) view.scale(1 / unity.width(), 1 / unity.height()) viewrect = view.viewport().rect() scenerect = view.transform().mapRect(rect) factor = min(viewrect.width() / scenerect.width(), viewrect.height() / scenerect.height()) view.scale(factor, factor) self._view.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self._view.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self._labels.values_update({"zoom": self.zoom}) def eventFilter(self, source, event): # ref: https://doc.qt.io/qt-5/qevent.html if source is self._scene: if type(event) is QtWidgets.QGraphicsSceneMouseEvent: return self.on_viewport_mouse(event) elif type(event) is QtGui.QEnterEvent: return self.on_enter(event) elif (event.type() == QtCore.QEvent.Leave): return self.on_leave(event) elif source is self._view.viewport(): # Catch context menu if type(event) == QtGui.QContextMenuEvent: return self.on_context_menu(event) elif event.type() == QtCore.QEvent.Wheel: return self.on_wheel(event) elif event.type() == QtCore.QEvent.ToolTip: return True return False def on_enter(self, event): # print("enter") return False def on_leave(self, event): # print("leave") return False def on_wheel(self, event): if self._qim is not None: view = self._view if event.angleDelta().y() > 0: factor = 1.25 else: factor = 0.8 view.scale(factor, factor) self._labels.values_update({"zoom": self.zoom}) return True def on_viewport_mouse(self, event): if event.type() == QtCore.QEvent.GraphicsSceneMouseMove: # print("viewport_mouse") self.on_mouse_move(event) return True elif (event.type() == QtCore.QEvent.GraphicsSceneMousePress and event.button() == Qt.LeftButton): self.on_mouse_left_press(event) return True elif (event.type() == QtCore.QEvent.GraphicsSceneMouseRelease and event.button() == Qt.LeftButton): self.on_mouse_left_release(event) return True elif (event.type() == QtCore.QEvent.GraphicsSceneMouseDoubleClick and event.button() == Qt.LeftButton): self.on_mouse_double_left_click(event) return True else: # print("Uncatched mouse event", event.type()) return False def on_mouse_left_press(self, event): self._drawing_rect = True self._dragging_rect = False self._rect_pos0 = event.scenePos() def on_mouse_left_release(self, event): if self._drawing_rect: self._rect_pos1 = event.scenePos() if (self._rect_pos0 == self._rect_pos1): self._group.removeFromGroup(self._rect) self._rect = None print("cancel drawing RECT") self.cancel_drawing_rect() else: print("finish drawing RECT", self._rect_pos0, self._rect_pos1) self.publish_drawing_rect() self._drawing_rect = False def cancel_drawing_rect(self, dclick=False): if self._qim is None: return keys = ["x", "y", "dx"] if dclick: keys = ["x", "y", "dx", "xy_ratio"] # resets everything except the zoom ratio for key in keys: #, "xy_ratio"]: value = self._fractal_zoom_init[key] if value is not None: # Send a model modification request # TODO: avoid update cancel xy_ratio 1.0 <class 'str'> print("update cancel", key, value, type(value)) self._presenter[key] = value def publish_drawing_rect(self): print("------*----- publish zoom") if self._qim is None: return # print("publish", self._rect_pos0, self._rect_pos1) # print("fractal", self._presenter["fractal"]) # print("fractal", self._presenter["image"]) nx = self._fractal_zoom_init["nx"] ny = self._fractal_zoom_init["ny"] # new center offet in pixel center_off_px = 0.5 * (self._rect_pos0.x() + self._rect_pos1.x() - nx) center_off_py = 0.5 * (ny - self._rect_pos0.y() - self._rect_pos1.y()) dx_pix = abs(self._rect_pos0.x() - self._rect_pos1.x()) # print("center px", center_off_px) # print("center py", center_off_py) ref_zoom = self._fractal_zoom_init.copy() # str -> mpf as needed to_mpf = {k: isinstance(self._fractal_zoom_init[k], str) for k in ["x", "y", "dx"]} # We may need to increase the dps to hold sufficent digits if to_mpf["dx"]: ref_zoom["dx"] = mpmath.mpf(ref_zoom["dx"]) pix = ref_zoom["dx"] / float(ref_zoom["nx"]) with mpmath.workdps(6): # Sets the working dps to 10e-8 x pixel size ref_zoom["dps"] = int(-mpmath.log10(pix * dx_pix / nx) + 8) print("------*----- NEW dps from zoom", ref_zoom["dps"]) # if ref_zoom["dps"] > mpmath.dps: # zoom_dps = max(ref_zoom["dps"], mpmath.mp.dps) with mpmath.workdps(ref_zoom["dps"]): for k in ["x", "y"]: if to_mpf[k]: ref_zoom[k] = mpmath.mpf(ref_zoom[k]) # print("is_mpf", to_mpf, ref_zoom) ref_zoom["x"] += center_off_px * pix ref_zoom["y"] += center_off_py * pix ref_zoom["dx"] = dx_pix * pix # mpf -> str (back) for (k, v) in to_mpf.items(): if v: if k == "dx": ref_zoom[k] = mpmath.nstr(ref_zoom[k], 16) else: ref_zoom[k] = str(ref_zoom[k]) for key in ["x", "y", "dx", "dps"]: self._presenter[key] = ref_zoom[key] # keys = ["x", "y", "dx"] # if dclick: # keys = ["x", "y", "dx", "xy_ratio"] # # resets everything except the zoom ratio # for key in keys: #, "xy_ratio"]: # value = self._fractal_zoom_init[key] # if value is not None: # # Send a model modification request # # TODO: avoid update cancel xy_ratio 1.0 <class 'str'> # print("update cancel", key, value, type(value)) # self._presenter[key] = value def on_mouse_double_left_click(self, event): self.fit_image() self.cancel_drawing_rect(dclick=True) def on_mouse_move(self, event): scene_pos = event.scenePos() self._labels.values_update({"px": scene_pos.x(), "py": scene_pos.y()}) if self._drawing_rect: self._dragging_rect = True self._rect_pos1 = event.scenePos() self.draw_rect(self._rect_pos0, self._rect_pos1) def draw_rect(self, pos0, pos1): """ Draws the selection rectangle """ # Enforce the correct ratio diffx = pos1.x() - pos0.x() diffy = pos1.y() - pos0.y() radius_sq = diffx ** 2 + diffy ** 2 diffx0 = math.sqrt(radius_sq / (1. + self.xy_ratio ** 2)) diffy0 = diffx0 * self.xy_ratio diffx0 = math.copysign(diffx0, diffx) diffy0 = math.copysign(diffy0, diffy) pos1 = QtCore.QPointF(pos0.x() + diffx0, pos0.y() + diffy0) topleft = QtCore.QPointF(min(pos0.x(), pos1.x()), min(pos0.y(), pos1.y())) bottomRight = QtCore.QPointF(max(pos0.x(), pos1.x()), max(pos0.y(), pos1.y())) rectF = QtCore.QRectF(topleft, bottomRight) if self._rect is not None: self._rect.setRect(rectF) else: self._rect = QGraphicsRectItem(rectF) self._rect.setPen(QtGui.QPen(QtGui.QColor("red"), 0, Qt.DashLine)) self._group.addToGroup(self._rect) def model_event_slot(self, keys, val): """ A model item has been modified - will it impact the widget ? """ # Find the mathching "mapping" - None if no match mapped = next((k for k, v in self._mapping.items() if v == keys), None) if mapped in ["image", "fractal"]: self.reset_im() elif mapped in ["x", "y", "dx", "xy_ratio", "dps"]: pass else: if mapped is not None: raise NotImplementedError("Mapping event not implemented: " + "{}".format(mapped))
class Imageobj(QGraphicsView): leftMouseButtonPressed = pyqtSignal(float, float) rightMouseButtonPressed = pyqtSignal(float, float) leftMouseButtonReleased = pyqtSignal(float, float) rightMouseButtonReleased = pyqtSignal(float, float) leftMouseButtonDoubleClicked = pyqtSignal(float, float) rightMouseButtonDoubleClicked = pyqtSignal(float, float) def __init__(self): QGraphicsView.__init__(self) # Image is displayed as a QPixmap in a QGraphicsScene attached to this QGraphicsView. self.scene = QGraphicsScene() self.setScene(self.scene) self.rectgroup = QGraphicsItemGroup() self.linegroup = QGraphicsItemGroup() self._pixmapHandle = None self.canZoom = True self.canPan = True self.zoomStack = [] self.aspectRatioMode = Qt.KeepAspectRatio self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.begin = QPoint() self.end = QPoint() self.helper_bool = False self.helper_bool2 = False self.rect = QGraphicsRectItem() self.line = QGraphicsLineItem() def hasImage(self): """ Returns whether or not the scene contains an image pixmap. """ return self._pixmapHandle is not None def pixmap(self): """ Returns the scene's current image pixmap as a QPixmap, or else None if no image exists. :rtype: QPixmap | None """ if self.hasImage(): return self._pixmapHandle.pixmap() return None def image(self): """ Returns the scene's current image pixmap as a QImage, or else None if no image exists. :rtype: QImage | None """ if self.hasImage(): return self._pixmapHandle.pixmap().toImage() return None def setImage(self, image): """ Set the scene's current image pixmap to the input QImage or QPixmap. Raises a RuntimeError if the input image has type other than QImage or QPixmap. :type image: QImage | QPixmap """ if type(image) is QPixmap: pixmap = image elif type(image) is QImage: pixmap = QPixmap.fromImage(image) else: raise RuntimeError( "ImageViewer.setImage: Argument must be a QImage or QPixmap.") if self.hasImage(): self._pixmapHandle.setPixmap(pixmap) else: self._pixmapHandle = self.scene.addPixmap(pixmap) self.setSceneRect(QRectF( pixmap.rect())) # Set scene size to image size. self.updateViewer() def loadImageFromFile(self, fileName=""): """ Load an image from file. Without any arguments, loadImageFromFile() will popup a file dialog to choose the image file. With a fileName argument, loadImageFromFile(fileName) will attempt to load the specified image file directly. """ fileDir = os.path.dirname(os.path.realpath('__file__')) results_dir = os.path.join(fileDir, 'results\\200_frames_of_bloodflow_results') if len(fileName) == 0: if QT_VERSION_STR[0] == '4': fileName = QFileDialog.getOpenFileName(self, "Open image file.") elif QT_VERSION_STR[0] == '5': fileName, dummy = QFileDialog.getOpenFileName( self, "Open image file.") if len(fileName) and os.path.isfile(fileName): imageName, file_data = self.create_image(fileName, results_dir) image = QImage(imageName) self.setImage(image) print(fileName) def Extract_image_and_metadata(self, file_data): global file_metadata im2show_path = file_data["im2show_path"] metadata_path = file_data["metadata_path"] # fileName = file_data["fileName"] filepath = file_data["path"] if os.path.isfile(metadata_path): with open(metadata_path, 'r') as fp: file_metadata = json.load(fp) else: with tifffile.TiffFile(filepath) as tif: file_metadata = tif.scanimage_metadata with open(metadata_path, 'w') as fp: json.dump(file_metadata, fp) if not os.path.isfile(im2show_path): tif_original = tifffile.imread(filepath) # first_five = tif_full_original[1:10:2]#selecting 5 image from tif, only vessels tif_original_size = tif_original.shape if len(tif_original_size) == 2: tif = tif_original.copy() arr = tif else: first_five = tif_original[1:10:2].copy() if file_metadata["SI.hScan2D.bidirectional"]: num_im, w, h = first_five.shape tif = np.zeros((num_im, int(h / 2), w)) for i in range(num_im): tif[i] = first_five[i][::2] else: tif = tif_original.copy() arr = tif[0] tif_size = tif.shape print(tif_size) # image5 = (tif[0]/5)+(tif[1]/5)+(tif[2]/5)+(tif[3]/5)+(tif[4]/5) # arr_avg=np.array(np.round(image5),dtype=np.int16) #round the pixels values # arr = tif[0] im2show = Image.fromarray(arr) try: im2show.save(im2show_path) except OSError: im2show = im2show.convert(mode='I') im2show.save(im2show_path) return file_metadata def updateViewer(self): """ Show current zoom (if showing entire image, apply current aspect ratio mode). """ if not self.hasImage(): return if len(self.zoomStack) and self.sceneRect().contains( self.zoomStack[-1]): self.fitInView(self.zoomStack[-1], Qt.IgnoreAspectRatio ) # Show zoomed rect (ignore aspect ratio). else: self.zoomStack = [ ] # Clear the zoom stack (in case we got here because of an invalid zoom). self.fitInView( self.sceneRect(), self.aspectRatioMode ) # Show entire image (use current aspect ratio mode). def resizeEvent(self, event): """ Maintain current zoom on resize. """ self.updateViewer() def mousePressEvent(self, event): """ Start mouse pan or zoom mode. """ scenePos = self.mapToScene(event.pos()) if event.button() == Qt.LeftButton: if self.canPan and self.helper_bool: self.setDragMode(QGraphicsView.RubberBandDrag) elif self.canPan and self.helper_bool2: self.setDragMode(QGraphicsView.ScrollHandDrag) self.start = scenePos #QGraphicsView.mouseMoveEvent(self,event) #self.setMouseTracking(True) self.leftMouseButtonPressed.emit(scenePos.x(), scenePos.y()) # self.cursorStartPosition = self.leftMouseButtonPressed.emit(scenePos.x(), scenePos.y()) # self.start = QPoint(self.cursorStartPosition.x(),self.cursorStartPosition.y()) elif event.button() == Qt.RightButton: if self.canZoom: self.setDragMode(QGraphicsView.RubberBandDrag) self.rightMouseButtonPressed.emit(scenePos.x(), scenePos.y()) QGraphicsView.mousePressEvent(self, event) def mouseReleaseEvent(self, event): """ Stop mouse pan or zoom mode (apply zoom if valid). """ QGraphicsView.mouseReleaseEvent(self, event) scenePos = self.mapToScene(event.pos()) if event.button() == Qt.LeftButton: if self.helper_bool: viewBBox = self.zoomStack[-1] if len( self.zoomStack) else self.sceneRect() selectionBBox = self.scene.selectionArea().boundingRect( ).intersected(viewBBox) if selectionBBox.isValid(): self.rect.setRect(selectionBBox) self.rect.setPen(QPen(Qt.white)) self.scene.addItem(self.rect) elif self.helper_bool2: viewBBox = self.zoomStack[-1] if len( self.zoomStack) else self.sceneRect() self.cursorCurrentPosition = scenePos self.current = QPointF(self.cursorCurrentPosition.x(), self.cursorCurrentPosition.y()) pen = QPen(Qt.red, 1, Qt.SolidLine) self.line.setLine(QLineF(self.start, self.current)) self.line.setPen(pen) self.scene.addItem(self.line) self.setDragMode(QGraphicsView.NoDrag) self.leftMouseButtonReleased.emit(scenePos.x(), scenePos.y()) elif event.button() == Qt.RightButton: if self.canZoom: viewBBox = self.zoomStack[-1] if len( self.zoomStack) else self.sceneRect() selectionBBox = self.scene.selectionArea().boundingRect( ).intersected(viewBBox) self.scene.setSelectionArea( QPainterPath()) # Clear current selection area. if selectionBBox.isValid() and (selectionBBox != viewBBox): self.zoomStack.append(selectionBBox) self.updateViewer() self.setDragMode(QGraphicsView.NoDrag) self.rightMouseButtonReleased.emit(scenePos.x(), scenePos.y()) # self.scene.addItem(self.group) self.updateViewer() def mouseDoubleClickEvent(self, event): """ Show entire image. """ scenePos = self.mapToScene(event.pos()) if event.button() == Qt.LeftButton: self.leftMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y()) elif event.button() == Qt.RightButton: if self.canZoom: self.zoomStack = [] # Clear zoom stack. self.updateViewer() self.rightMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y()) QGraphicsView.mouseDoubleClickEvent(self, event) def approve_obj(self): self.scene.removeItem(self.rect) self.scene.removeItem(self.line) viewBBox = self.zoomStack[-1] if len( self.zoomStack) else self.sceneRect() selectionBBox = self.scene.selectionArea().boundingRect().intersected( viewBBox) rect = QGraphicsRectItem() rect.setRect(selectionBBox) rect.setPen(QPen(Qt.green)) self.rectgroup.addToGroup(rect) self.scene.addItem(self.rectgroup) line = QGraphicsLineItem() line.setLine(QLineF(self.start, self.current)) line.setPen(QPen(Qt.green)) self.linegroup.addToGroup(line) self.scene.addItem(self.linegroup) return (selectionBBox, self.start, self.current) def create_fromDB(self, pos_list=[], obj_type=""): if obj_type == "rois": num_rois = len(pos_list) for i in range(num_rois): points = pos_list[i] rect = QGraphicsRectItem() rect.setPen(QPen(Qt.green)) rect.setRect(points[0], points[1], points[2], points[3]) self.rectgroup.addToGroup(rect) self.scene.addItem(self.rectgroup) elif obj_type == "vector": num_vec = len(pos_list) for i in range(num_vec): points = pos_list[i] vec = QGraphicsLineItem() vec.setPen(QPen(Qt.green)) vec.setLine(points[0][0], points[0][1], points[1][0], points[1][1]) self.linegroup.addToGroup(vec) self.scene.addItem(self.linegroup) def mark_item(self, item_num): rect_items = self.rectgroup.childItems() line_items = self.linegroup.childItems() rect_item = rect_items[item_num] line_item = line_items[item_num] rect_item.setPen(QPen(Qt.red)) line_item.setPen(QPen(Qt.red)) rect_items.remove(rect_item) line_items.remove(line_item) for i in rect_items: i.setPen(QPen(Qt.green)) for i in line_items: i.setPen(QPen(Qt.green)) def delete_roi(self, num): self.scene.removeItem(self.rectgroup) self.scene.removeItem(self.linegroup) rect_item = self.rectgroup.childItems()[num] line_item = self.linegroup.childItems()[num] self.rectgroup.removeFromGroup(rect_item) self.linegroup.removeFromGroup(line_item) self.scene.addItem(self.rectgroup) self.scene.addItem(self.linegroup) def restart_obj(self): self.scene.removeItem(self.rectgroup) self.scene.removeItem(self.linegroup) rect_items = self.rectgroup.childItems() line_items = self.linegroup.childItems() for rect_item in rect_items: self.rectgroup.removeFromGroup(rect_item) for line_item in line_items: self.linegroup.removeFromGroup(line_item)
class Ui_MainWindow(QtWidgets.QMainWindow, Ui_MainWindowBase): def __init__(self): super(Ui_MainWindow, self).__init__() self.setupUi(self) self.videoPlaybackInit() self.imgInit() self.menuInit() self.df = {} self.trackingPathGroup = None self.movableArrowGroup = None self.line_data_dict = {} self.line_item_dict = {} self.file_name_dict = {} self.currentFrameNo = 0 self.colors = None self.overlayCheckBox.stateChanged.connect( self.overlayCheckBoxStateChanged) self.radiusSpinBox.valueChanged.connect(self.radiusSpinBoxValueChanged) self.frameNoSpinBox.valueChanged.connect( self.frameNoSpinBoxValueChanged) self.markDeltaSpinBox.valueChanged.connect( self.markDeltaSpinBoxValueChanged) def overlayCheckBoxStateChanged(self, s): if self.overlayCheckBox.isChecked(): self.frameBufferItemGroup.show() else: self.frameBufferItemGroup.hide() self.updateInputGraphicsView() def markDeltaSpinBoxValueChanged(self, value): if self.trackingPathGroup is not None: self.trackingPathGroup.setMarkDelta(self.markDeltaSpinBox.value()) self.updateInputGraphicsView() def radiusSpinBoxValueChanged(self, value): if self.trackingPathGroup is not None: self.trackingPathGroup.setRadius(self.radiusSpinBox.value()) self.updateInputGraphicsView() def frameNoSpinBoxValueChanged(self, value): if self.trackingPathGroup is not None: self.trackingPathGroup.setOverlayFrameNo( self.frameNoSpinBox.value()) self.updateInputGraphicsView() def dragEnterEvent(self, event): event.acceptProposedAction() def dropEvent(self, event): # event.setDropAction(QtCore.Qt.MoveAction) mime = event.mimeData() if mime.hasUrls(): urls = mime.urls() if len(urls) > 0: self.processDropedFile(urls[0].toLocalFile()) event.acceptProposedAction() def processDropedFile(self, filePath): root, ext = os.path.splitext(filePath) if ext == ".filter": # Read Filter self.openFilterFile(filePath=filePath) return elif ext == ".csv": self.openCSVFile(filePath=filePath) elif ext == ".json": self.openJSONFile(filePath=filePath) elif ext == ".color": self.openColorFile(filePath=filePath) elif self.openImageFile(filePath=filePath): return elif self.openVideoFile(filePath=filePath): return def videoPlaybackInit(self): self.videoPlaybackWidget.hide() self.videoPlaybackWidget.frameChanged.connect(self.setFrame, Qt.QueuedConnection) def setFrame(self, frame, frameNo): if frame is not None: self.cv_img = frame self.currentFrameNo = frameNo self.updateInputGraphicsView() self.evaluate() def imgInit(self): self.cv_img = cv2.imread( os.path.join(sampleDataPath, "color_filter_test.png")) self.frameBuffer = Queue() self.frameBufferItemGroup = QGraphicsItemGroup() self.frameBufferItemGroup.hide() self.inputPixmapRenderScene = QGraphicsScene() self.inputPixmapRenderScene.addItem(self.frameBufferItemGroup) self.inputScene = QGraphicsScene() self.inputGraphicsView.setScene(self.inputScene) self.inputGraphicsView.resizeEvent = self.graphicsViewResized # self.inputScene.addItem(self.frameBufferItemGroup) qimg = misc.cvMatToQImage(self.cv_img) self.inputPixmap = QPixmap.fromImage(qimg) self.inputPixmapItem = QGraphicsPixmapItem(self.inputPixmap) self.inputScene.addItem(self.inputPixmapItem) self.rubberBand = QtWidgets.QRubberBand( QtWidgets.QRubberBand.Rectangle, self.inputGraphicsView) self.inputGraphicsView.mousePressEvent = self.inputGraphicsViewMousePressEvent self.inputGraphicsView.mouseMoveEvent = self.inputGraphicsViewMouseMoveEvent self.inputGraphicsView.mouseReleaseEvent = self.inputGraphicsViewMouseReleaseEvent self.inputGraphicsView.viewport().installEventFilter(self) self.inputGraphicsView.setMouseTracking(True) self.overlayScene = QGraphicsScene() self.inputGraphicsView.setOverlayScene(self.overlayScene) self.zoomedGraphicsView.setScene(self.inputScene) self.zoomedGraphicsView.setOverlayScene(self.overlayScene) def inputGraphicsViewMousePressEvent(self, event): self.origin = QPoint(event.pos()) self.rubberBand.setGeometry(QtCore.QRect(self.origin, QtCore.QSize())) self.rubberBand.show() # Comment out to permit the view for sending the event to the child scene. # QGraphicsView.mousePressEvent(self.inputGraphicsView, event) def inputGraphicsViewMouseMoveEvent(self, event): if self.rubberBand.isVisible(): self.rubberBand.setGeometry( QtCore.QRect(self.origin, event.pos()).normalized()) # Comment out to permit the view for sending the event to the child scene. # QGraphicsView.mouseMoveEvent(self.inputGraphicsView, event) def inputGraphicsViewMouseReleaseEvent(self, event): if self.rubberBand.isVisible(): self.rubberBand.hide() rect = self.rubberBand.geometry() sceneRect = self.inputGraphicsView.mapToScene(rect).boundingRect() self.zoomedGraphicsView.fitInView(QRectF(sceneRect)) self.zoomedGraphicsView.viewport().update() # Comment out to permit the view for sending the event to the child scene. self.inputGraphicsView.viewport().update() # QGraphicsView.mouseReleaseEvent(self.inputGraphicsView, event) def menuInit(self): self.actionSaveDataFiles.triggered.connect(self.saveDataFiles) self.actionOpenCSVFile.triggered.connect(self.openCSVFile) self.actionOpenCSVFile.triggered.connect(self.openJSONFile) self.actionOpenCSVFile.triggered.connect(self.openJSONFile) self.actionPath.triggered.connect(self.actionPathTriggered) self.actionCircle.triggered.connect(self.actionCircleTriggered) self.actionIntervalMark.triggered.connect( self.actionIntervalMarkTriggered) self.actionShape.triggered.connect(self.actionShapeTriggered) self.actionSkeleton.triggered.connect(self.actionSkeletonTriggered) self.actionArrow.triggered.connect(self.actionArrowTriggered) self.actionChangeOrderOfNum.triggered.connect( self.actionChangeOrderOfNumTriggered) self.actionTrackingPathColor.triggered.connect( self.openTrackingPathColorSelectorDialog) def actionPathTriggered(self, checked): if self.trackingPathGroup is not None: self.trackingPathGroup.setDrawLine(checked) if not checked or self.actionIntervalMark.isChecked(): self.trackingPathGroup.setDrawMarkItem(checked) self.updateInputGraphicsView() def actionCircleTriggered(self, checked): if self.trackingPathGroup is not None: self.trackingPathGroup.setDrawItem(checked) self.updateInputGraphicsView() def actionIntervalMarkTriggered(self, checked): if self.trackingPathGroup is not None: self.trackingPathGroup.setDrawMarkItem(checked) self.updateInputGraphicsView() def actionShapeTriggered(self, checked): if 'shape' in self.line_item_dict.keys(): line_item = self.line_item_dict['shape'] if checked: line_item.show() else: line_item.hide() def actionSkeletonTriggered(self, checked): if 'skeleton' in self.line_item_dict.keys(): line_item = self.line_item_dict['skeleton'] if checked: line_item.show() else: line_item.hide() def actionArrowTriggered(self, checked): if self.movableArrowGroup is not None: if checked: self.movableArrowGroup.show() else: self.movableArrowGroup.hide() def actionChangeOrderOfNumTriggered(self, checked): if len(self.df.keys()) != 0 or len(self.line_data_dict.keys()) != 0: self.videoPlaybackWidget.stop() dialog = DataSwapDialog(self) dialog.setWindowModality(Qt.WindowModal) dialog.setData(self.df, self.line_data_dict) dialog.swapAccepted.connect(self.evaluate) res = dialog.exec() def openTrackingPathColorSelectorDialog(self, activated=False): if self.trackingPathGroup is not None: self.trackingPathGroup.openColorSelectorDialog(self) def openVideoFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Video File', userDir) if len(filePath) is not 0: self.filePath = filePath ret = self.videoPlaybackWidget.openVideo(filePath) if ret == False: return False self.videoPlaybackWidget.show() self.cv_img = self.videoPlaybackWidget.getCurrentFrame() self.initialize() return True else: return False def openImageFile(self, activated=False, filePath=None): if filePath == None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Image File', userDir) if len(filePath) is not 0: self.filePath = filePath img = cv2.imread(filePath) if img is None: return False self.cv_img = img self.videoPlaybackWidget.hide() self.updateInputGraphicsView() self.evaluate() return True else: return False def openCSVFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open CSV File', userDir, 'CSV files (*.csv)') if len(filePath) is not 0: df = pd.read_csv(filePath, index_col=0) name = df.index.name if name is None: name = 'position' self.df[name] = df self.file_name_dict[name] = filePath if name is None or name == 'position': if self.trackingPathGroup is not None: self.inputScene.removeItem(self.trackingPathGroup) self.trackingPathGroup = TrackingPathGroup() self.trackingPathGroup.setRect(self.inputScene.sceneRect()) self.inputScene.addItem(self.trackingPathGroup) self.trackingPathGroup.setDrawLine(self.actionPath.isChecked()) self.trackingPathGroup.setDrawItem( self.actionCircle.isChecked()) self.trackingPathGroup.setDrawMarkItem( self.actionIntervalMark.isChecked()) shape = self.df['position'].shape self.num_items = int(shape[1] / 2) index = (np.repeat(range(self.num_items), 2).tolist(), [0, 1] * self.num_items) self.df['position'].columns = pd.MultiIndex.from_tuples( tuple(zip(*index))) self.trackingPathGroup.setDataFrame(self.df['position']) delta = self.df['position'].index[1] - self.df[ 'position'].index[0] self.videoPlaybackWidget.setPlaybackDelta(delta) self.videoPlaybackWidget.setMaxTickableFrameNo( self.df['position'].index[-1]) elif name == 'arrow': if self.movableArrowGroup is not None: self.inputScene.removeItem(self.movableArrowGroup) self.movableArrowGroup = MovableArrowGroup() self.inputScene.addItem(self.movableArrowGroup) self.movableArrowGroup.edited.connect(self.arrowEdited) if not self.actionArrow.isChecked(): self.movableArrowGroup.hide() if 'arrow' in self.df.keys() and 'position' in self.df.keys(): self.movableArrowGroup.setDataFrame(self.df['arrow'], self.df['position']) self.initialize() def openColorFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Color File', userDir, 'Color files (*.color)') if len(filePath) is not 0: self.colors = pd.read_csv(filePath, index_col=0).values.tolist() self.colors = [QColor(*rgb) for rgb in self.colors] self.setColorsToGraphicsObjects() def openJSONFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open JSON File', userDir, 'JSON files (*.json)') if len(filePath) is not 0: with open(filePath) as f_p: data = json.load(f_p) name = data['name'] self.line_data_dict[name] = data self.file_name_dict[name] = filePath if name in self.line_item_dict.keys(): self.inputScene.removeItem(self.line_item_dict[name]) lines = MovableLineGroup() lines.setData(data) lines.setRect(self.inputScene.sceneRect()) if name == 'shape' and not self.actionShape.isChecked(): lines.hide() if name == 'skeleton' and not self.actionSkeleton.isChecked(): lines.hide() self.line_item_dict[name] = lines self.inputScene.addItem(lines) self.initialize() def saveDataFiles(self, activated=False, filePath=None): if len(self.df.keys()) != 0: for k, v in self.df.items(): f_name, f_ext = os.path.splitext(self.file_name_dict[k]) candidate_file_path = '{0}-fixed{1}'.format(f_name, f_ext) filePath, _ = QFileDialog.getSaveFileName( None, 'Save CSV File', candidate_file_path, "CSV files (*.csv)") if len(filePath) is not 0: logger.debug("Saving CSV file: {0}".format(filePath)) df = v.copy() col_n = df.values.shape[1] / 2 col_names = np.array([('x{0}'.format(i), 'y{0}'.format(i)) for i in range(int(round(col_n))) ]).flatten() df.columns = pd.Index(col_names) df.index.name = k df.to_csv(filePath) for k, v in self.line_data_dict.items(): f_name, f_ext = os.path.splitext(self.file_name_dict[k]) candidate_file_path = '{0}-fixed{1}'.format(f_name, f_ext) filePath, _ = QFileDialog.getSaveFileName(None, 'Save JSON File', candidate_file_path, "JSON files (*.json)") if len(filePath) is not 0: logger.debug("Saving JSON file: {0}".format(filePath)) with open(filePath, 'w') as f_p: json.dump(v, f_p) def updateInputGraphicsView(self): self.inputScene.removeItem(self.inputPixmapItem) qimg = misc.cvMatToQImage(self.cv_img) self.inputPixmap = QPixmap.fromImage(qimg) p = QPainter(self.inputPixmap) sourceRect = self.inputPixmapRenderScene.sceneRect() self.inputPixmapRenderScene.render(p, QRectF(sourceRect), QRectF(sourceRect), QtCore.Qt.IgnoreAspectRatio) self.inputPixmapItem = QGraphicsPixmapItem(self.inputPixmap) rect = QtCore.QRectF(self.inputPixmap.rect()) self.inputScene.setSceneRect(rect) self.inputScene.addItem(self.inputPixmapItem) self.inputGraphicsView.viewport().update() self.graphicsViewResized() def eventFilter(self, obj, event): if event.type() == QEvent.Wheel: self.videoPlaybackWidget.wheelEvent(event) return True if event.type() == QEvent.KeyPress: if Qt.Key_Home <= event.key() <= Qt.Key_PageDown: self.videoPlaybackWidget.keyPressEvent(event) return True return False def graphicsViewResized(self, event=None): self.inputGraphicsView.fitInView( QtCore.QRectF(self.inputPixmap.rect()), QtCore.Qt.KeepAspectRatio) def setColorsToGraphicsObjects(self): # FIXME: データセットと色リストのサイズ整合性チェックが必要 if self.colors is not None: if self.trackingPathGroup is not None: self.trackingPathGroup.setColors(self.colors) for k, v in self.line_item_dict.items(): v.setColors(self.colors) def initialize(self): if not self.videoPlaybackWidget.isOpened(): return if self.trackingPathGroup is not None: r = self.trackingPathGroup.autoAdjustRadius(self.cv_img.shape) self.radiusSpinBox.setValue(r) self.trackingPathGroup.autoAdjustLineWidth(self.cv_img.shape) self.trackingPathGroup.setItemsAreMovable(True) if self.movableArrowGroup is not None: pass for k, v in self.line_item_dict.items(): v.autoAdjustLineWidth(self.cv_img.shape) v.autoAdjustMarkSize(self.cv_img.shape) self.setColorsToGraphicsObjects() self.evaluate() def evaluate(self): if not self.videoPlaybackWidget.isOpened(): return qimg = misc.cvMatToQImage(self.cv_img) pixmapItem = QGraphicsPixmapItem(QPixmap.fromImage(qimg)) pixmapItem.setOpacity(0.2) self.frameBuffer.put(pixmapItem) self.frameBufferItemGroup.addToGroup(pixmapItem) if self.frameBuffer.qsize() > 10: item = self.frameBuffer.get() self.frameBufferItemGroup.removeFromGroup(item) if self.trackingPathGroup is not None: self.trackingPathGroup.setPoints(self.currentFrameNo) if self.movableArrowGroup is not None: self.movableArrowGroup.setPositions(self.currentFrameNo) for k, v in self.line_item_dict.items(): v.setPolyline(self.currentFrameNo) @pyqtSlot(object) def arrowEdited(self, name): # TODO: 方向の再推定機能の実装 # quit_msg = "Arrow {} edited.\nRe-estimate the direction in following frames?".format(name) # reply = QtWidgets.QMessageBox.question( # self, # 'Question', # quit_msg, # QtWidgets.QMessageBox.Yes, # QtWidgets.QMessageBox.No # ) # # if reply == QtWidgets.QMessageBox.Yes: # pass # else: # pass pass
class NodeItem(QGraphicsItem): def __init__ (self, nodeobj, parent=None, view=None, state=1): super().__init__() self.edge = None self.linkIDs = None self.children = None self.childpos = None self.nodeobj = nodeobj self.style = FlGlob.mainwindow.style self.view = weakref.proxy(view) self.refID = parent.realid() if parent is not None else None self.state = state self.setrank(parent) self.setCursor(Qt.ArrowCursor) self.yoffset = 0 self.graphicsetup() self.setstate(state) def id (self): return (self.refID, self.nodeobj.ID) def realid (self): return self.nodeobj.ID def childlist (self, generate=False): ID = self.nodeobj.ID itemtable = self.view.itemtable if self.state == 1 and ID in itemtable and not self.iscollapsed(): if self.children and self.nodeobj.linkIDs == self.linkIDs and None not in [c() for c in self.children]: ret = self.children else: children = [] for child in self.nodeobj.linkIDs: if child in itemtable[ID]: item = itemtable[ID][child] else: continue children.append(weakref.ref(item)) self.linkIDs = self.nodeobj.linkIDs.copy() self.children = children ret = children else: ret = [] if generate: x = self.x() y = self.y() self.childpos = [] for target in ret: t = target() self.childpos.append((t.x()+t.boundingRect().left()-self.style.activemargin-x, t.y()-y)) if self.edge: if self.childpos != self.edge.childpos: self.edge.prepareGeometryChange() self.edge.sourceright = self.boundingRect().right() self.edge.update(self.edge.boundingRect()) return ret def setedge (self, edge): self.edge = edge edge.setX(self.x()) def setactive (self, active): if active: self.activebox.show() self.mainbox.setBrush(QBrush(self.altcolor)) else: self.activebox.hide() self.mainbox.setBrush(QBrush(self.maincolor)) def setselected (self, selected): if selected: self.selectbox.show() else: self.selectbox.hide() def setstate (self, state): self.state = state if state == 1: # normal self.show() self.graphgroup.setOpacity(1) self.shadowbox.show() elif state == 0: # ghost self.show() self.graphgroup.setOpacity(0.7) self.shadowbox.hide() elif state == -1: # hidden self.hide() def setplaymode (self, playmode): if playmode: self.setOpacity(0.5) else: self.setOpacity(1) def setY (self, y): parent = self.view.itembyID(self.refID) y += self.getyoffset() if self.edge is not None: self.edge.setY(y) super().setY(y) def setrank (self, parent): if parent is None: return if self.issubnode(): x = parent.x() self.setX(x) else: x = parent.x()+self.style.rankwidth self.setX(x) self.nudgechildren() if self.edge is not None: self.edge.setX(x) def nudgechildren (self): for child in self.childlist(): child().setrank(self) def getyoffset (self): if self.nodeobj.nodebank == -1: return self.yoffset else: return self.view.itembyID(self.refID).getyoffset() + self.yoffset def hide (self): super().hide() if self.edge: self.edge.hide() def show (self): super().show() if self.edge: self.edge.show() def issubnode (self): return self.nodeobj.nodebank is not -1 def isghost (self): return not self.state def realnode (self): return self.view.itembyID(self.nodeobj.ID) def isactive (self): return self.view.activenode is self def isselected (self): return self.view.selectednode is self def iscollapsed (self): return self.id() in self.view.collapsednodes def y_top (self): return self.y() - self.boundingRect().height()//2 def y_bottom (self): return self.y() + self.boundingRect().height()//2 def bulkshift (self, children, diff): self.setY(self.y() + diff) if children is None: children = [c() for c in self.childlist()] for child in children: child.bulkshift(None, diff) def treeposition (self, ranks=None): if ranks is None: ranks = dict() localranks = dict() children = [c() for c in self.childlist()] for child in children: localranks = child.treeposition(localranks) rank = self.x() // self.style.rankwidth if children: top = children[0].y_top() bottom = children[-1].y_bottom() self.setY((top+bottom)//2) localranks[rank] = [self.y_top, self.y_bottom] streeshift = None for r in localranks: if r in ranks: rankshift = ranks[r][1]() + self.style.rowgap - localranks[r][0]() if streeshift is None or rankshift > streeshift: streeshift = rankshift ranks[r][1] = localranks[r][1] else: ranks[r] = localranks[r] if streeshift: self.bulkshift(children, streeshift) return ranks def siblings (self): if self.refID is None: return None parent = self.view.nodecontainer.nodes[self.refID] if self.issubnode(): return parent.subnodes else: return parent.linkIDs def siblingabove (self): sibs = self.siblings() if sibs is None or self.nodeobj.ID not in sibs: return None myindex = sibs.index(self.nodeobj.ID) if myindex: sibID = (self.refID, sibs[myindex-1]) return self.view.itembyfullID(sibID) else: return None def siblingbelow (self): sibs = self.siblings() if sibs is None or self.nodeobj.ID not in sibs: return None myindex = sibs.index(self.nodeobj.ID) if len(sibs) > myindex+1: sibID = (self.refID, sibs[myindex+1]) return self.view.itembyfullID(sibID) else: return None def subtreesize (self, depth=-1): """Find vertical extents of a subtree. Returns min/max y coordinates up to given depth (negative depth means whole subtree).""" # calculate child positions for EgdeItem only once when calculating scenerect if depth<0: generate = True else: generate = False children = [c() for c in self.childlist(generate=generate)] maxdepth = abs(depth) if children and depth: nextdepth = depth-1 ymin = self.y_top() ymax = self.y_bottom() for child in children: top, bottom, depth = child.subtreesize(nextdepth) ymin = min(ymin, top) ymax = max(ymax, bottom) maxdepth = max(maxdepth, depth) else: ymin = self.y_top() ymax = self.y_bottom() return ymin, ymax, maxdepth def boundingRect (self): return self.rect def paint (self, painter, style, widget): pass def pixmap (self, path): return QPixmap(path).scaledToWidth(self.style.boldheight, Qt.SmoothTransformation) def graphicsetup (self): lightbrush = QBrush(FlPalette.light) mainbrush = QBrush(self.maincolor) altbrush = QBrush(self.altcolor) nopen = QPen(0) viewport = self.view.viewport() self.graphgroup = QGraphicsItemGroup(self) self.fggroup = QGraphicsItemGroup(self) self.shadowbox = QGraphicsRectItem(self) self.shadowbox.setCacheMode(QGraphicsItem.DeviceCoordinateCache) self.shadowbox.setBrush(FlPalette.dark) self.shadowbox.setPen(nopen) self.shadowbox.setPos(*(self.style.shadowoffset,)*2) self.graphgroup.addToGroup(self.shadowbox) self.activebox = QGraphicsRectItem(self) self.activebox.setCacheMode(QGraphicsItem.DeviceCoordinateCache) activepen = QPen(self.maincolor, self.style.selectmargin, join=Qt.MiterJoin) self.activebox.setPen(activepen) self.activebox.hide() self.graphgroup.addToGroup(self.activebox) self.selectbox = QGraphicsRectItem(self) self.selectbox.setCacheMode(QGraphicsItem.DeviceCoordinateCache) selectpen = QPen(FlPalette.light, self.style.selectmargin, join=Qt.MiterJoin) self.selectbox.setPen(selectpen) self.selectbox.hide() self.graphgroup.addToGroup(self.selectbox) self.mainbox = QGraphicsRectItem(self) self.mainbox.setCacheMode(QGraphicsItem.DeviceCoordinateCache) self.mainbox.setBrush(mainbrush) self.mainbox.setPen(nopen) self.graphgroup.addToGroup(self.mainbox) self.nodelabel = QGraphicsSimpleTextItemCond(self, viewport) self.nodelabel.setBrush(lightbrush) self.nodelabel.setFont(self.style.boldfont) self.nodelabel.setText(self.label % self.realid()) self.nodelabel.setPos(self.style.itemmargin, self.style.itemmargin) self.fggroup.addToGroup(self.nodelabel) self.icon = self.pixmap("images/blank.png") self.iwidth = self.icon.width() self.iconx = self.style.nodetextwidth self.condicon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.condicon.setPos(self.iconx-self.iwidth, self.style.itemmargin) self.iconx = self.condicon.x() self.fggroup.addToGroup(self.condicon) self.randicon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.randicon.setPos(self.iconx-self.style.itemmargin-self.iwidth, self.style.itemmargin) self.iconx = self.randicon.x() self.fggroup.addToGroup(self.randicon) self.exiticon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.exiticon.setPos(self.iconx-self.style.itemmargin-self.iwidth, self.style.itemmargin) self.iconx = self.exiticon.x() self.fggroup.addToGroup(self.exiticon) self.entericon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.entericon.setPos(self.iconx-self.style.itemmargin-self.iwidth, self.style.itemmargin) self.iconx = self.entericon.x() self.fggroup.addToGroup(self.entericon) self.persisticon = QGraphicsPixmapItemCond(self.icon, self, viewport) self.persisticon.setPos(self.iconx-self.style.itemmargin-self.iwidth, self.style.itemmargin) self.iconx = self.persisticon.x() self.fggroup.addToGroup(self.persisticon) self.comment = QGraphicsTextItemCond(self, viewport) self.comment.setTextWidth(self.style.nodetextwidth) self.comment.setDefaultTextColor(FlPalette.light) self.comment.setPos(0, self.nodelabel.y()+self.nodelabel.boundingRect().height()+self.style.itemmargin) self.fggroup.addToGroup(self.comment) self.graphgroup.addToGroup(self.fggroup) self.view.nodedocs[self.realid()]["comment"].contentsChanged.connect(self.updatecomment) self.updatecondition() self.updateenterscripts() self.updateexitscripts() self.updaterandweight() self.updatepersistence() # Never call updatelayout() from here (or any inheritable reimplementation)! def collapse (self, collapse): for item in self.fggroup.childItems(): if item is not self.nodelabel: if collapse: item.hide() else: item.show() self.updatelayout() def updatecondition (self): icons = {True: "key", False: "blank"} pixmap = self.pixmap("images/%s.png" % icons[self.nodeobj.hascond()]) self.condicon.setPixmap(pixmap) if self.nodeobj.hascond(): self.condicon.setToolTip("Condition") else: self.condicon.setToolTip("") def updateenterscripts (self): icons = {True: "script-enter", False: "blank"} pixmap = self.pixmap("images/%s.png" % icons[self.nodeobj.hasenterscripts()]) self.entericon.setPixmap(pixmap) if self.nodeobj.hasenterscripts(): self.entericon.setToolTip("Enter Scripts") else: self.entericon.setToolTip("") def updateexitscripts (self): icons = {True: "script-exit", False: "blank"} pixmap = self.pixmap("images/%s.png" % icons[self.nodeobj.hasexitscripts()]) self.exiticon.setPixmap(pixmap) if self.nodeobj.hasexitscripts(): self.exiticon.setToolTip("Exit Scripts") else: self.exiticon.setToolTip("") def updaterandweight (self): icons = {True: "dice", False: "blank"} pixmap = self.pixmap("images/%s.png" % icons[bool(self.nodeobj.randweight)]) self.randicon.setPixmap(pixmap) if self.nodeobj.randweight: self.randicon.setToolTip("Random Weight: %s" % self.nodeobj.randweight) else: self.randicon.setToolTip("") def updatepersistence (self): icons = {"Mark": "mark", "OncePerConv": "once", "OnceEver": "onceever", "": "blank"} pixmap = self.pixmap("images/%s.png" % icons[self.nodeobj.persistence]) self.persisticon.setPixmap(pixmap) if self.nodeobj.persistence: self.persisticon.setToolTip("Persistence: %s" % self.nodeobj.persistence) else: self.persisticon.setToolTip("") def updatecomment (self): self.fggroup.removeFromGroup(self.comment) contents = self.view.nodedocs[self.realid()]["comment"].toPlainText() if not contents: self.comment.hide() else: self.comment.show() self.comment.setPlainText(contents) self.fggroup.addToGroup(self.comment) self.updatelayout() def updatelayout (self): if self.iscollapsed(): rect = self.nodelabel.mapRectToParent(self.nodelabel.boundingRect()) else: rect = self.fggroup.childrenBoundingRect() mainrect = rect.marginsAdded(self.style.nodemargins) self.mainbox.setRect(mainrect) self.shadowbox.setRect(mainrect) self.selectbox.setRect(mainrect.marginsAdded(self.style.selectmargins)) activerect = mainrect.marginsAdded(self.style.activemargins) self.activebox.setRect(activerect) self.graphgroup.setPos(-activerect.width()//2-activerect.x(), -activerect.height()//2-activerect.y()) self.prepareGeometryChange() self.rect = self.graphgroup.mapRectToParent(mainrect) self.view.updatelayout() def mouseDoubleClickEvent (self, event): super().mouseDoubleClickEvent(event) event.accept() if event.button() == Qt.LeftButton: self.view.setactivenode(self) def mousePressEvent (self, event): super().mousePressEvent(event) if event.button() & (Qt.LeftButton | Qt.RightButton) : self.view.setselectednode(self) event.accept() def __repr__ (self): return "<%s %s>" % (type(self).__name__, self.id())
class Node(QGraphicsItemGroup): """ Parent class for all types of nodes. It contains all necessary functions to print a node in the GUI """ def __init__(self, node, parent, background, border, text, x=0, y=0, offset=20): """ Constructor for the node class. It generates all necessary variables and calls the draw function :param node: data node which it gets the data from :param parent: parent widget :param background: background color of the node :param border: border color for the node :param text: text color for the node :param x: x-position of the node :param y: y-position of the node :param offset: offset for the type to center it """ super().__init__() self.typeOffset = offset self.node = node self.parent = parent self.threatEdge = None self.counterEdge = None self.defaultEdge = None self.childEdges = [] self.parentEdges = [] self.headerGroup = QGraphicsItemGroup() self.attributes = QGraphicsItemGroup() self.footerGroup = QGraphicsItemGroup() self.setFlag(QGraphicsItem.ItemIsMovable, True) self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.attributesHeight = 0 self.headerHeight = 0 self.printHeader(background, border, text) self.printAttributes(background, border, text) self.printFooter(background, border, text) self.setAcceptDrops(True) self.setPos(x, y) def getTypeRecursiveDown(self): """ Searches the children of a node to get a node with type != Conjunction If there is no other node with type != Conjunction, Conjunction will be returned :return: type of first child node with type != Conjunction or Conjunction """ if isinstance(self, Conjunction): for c in self.childEdges: if isinstance(c.dst, Conjunction): return c.dst.getTypeRecursiveDown() else: return type(c.dst) return type(self) else: return type(self) def getTypeRecursiveUp(self): """ Searches the parents of a node to get a node with type != Conjunction If there is no other node with type != Conjunction, Conjunction will be returned :return: type of first parent node with type != Conjunction or Conjunction """ if isinstance(self, Conjunction): for c in self.childEdges: if isinstance(c.dst, Conjunction): return c.dst.getTypeRecursiveUp() else: return type(c.dst) return type(self) else: return type(self) def addEdge(self, dst): """ Adds an child edge to this node an places the start of the arrow in the right place :param dst: destination node for the edge """ if isinstance(self, Threat) and dst.getTypeRecursiveDown() is Threat: edge = Edge(self, dst, -50) elif isinstance(self, Threat) and dst.getTypeRecursiveDown() is Countermeasure: edge = Edge(self, dst, 50) else: edge = Edge(self, dst, 0) self.parent.scene.addItem(edge) self.childEdges.append(edge) dst.parentEdges.append(self.childEdges[-1]) def actualizeEdges(self): """ Actualizes all child edges of this node so they start at the right position """ for e in self.childEdges: if isinstance(self, Threat) and e.dst.getTypeRecursiveDown() is Threat: e.offset = -50 elif isinstance(self, Threat) and e.dst.getTypeRecursiveDown() is Countermeasure: e.offset = 50 else: e.offset = 0 def fixParentEdgeRec(self): """ Fixes all starts of the parent edges so they start at the right position """ for p in self.parentEdges: if isinstance(p.start, Conjunction): p.start.fixParentEdgeRec() else: p.start.actualizeEdges() def getLeftRightChildren(self): """ Splits the children in to arrays with the same size :return: Tuple (left, right) with child elements split in to arrays """ left = [] right = [] neutralLeft = False for e in self.childEdges: if e.offset < 0: left.append(e.dst) elif e.offset > 0: right.append(e.dst) else: if neutralLeft is False: left.append(e.dst) neutralLeft = True else: right.append(e.dst) neutralLeft = False return left, right def printHeader(self, background, border, text): """ Prints the the header of the node. It contains the Node id, title and type :param background: background color of the node :param border: border color for the node :param text: text color for the node """ x = self.x() y = self.y() self.idText = QGraphicsTextItem() self.typeText = QGraphicsTextItem() self.titleText = QGraphicsTextItem() self.idRect = QGraphicsRectItem() self.typeRect = QGraphicsRectItem() self.titleRect = QGraphicsRectItem() self.typeText.setFont(Configuration.font) self.titleText.setFont(Configuration.font) self.idText.setFont(Configuration.font) self.typeText.setDefaultTextColor(QColor(text)) self.titleText.setDefaultTextColor(QColor(text)) self.idText.setDefaultTextColor(QColor(text)) self.titleText.setTextWidth(200) self.idText.setPlainText(self.node.id) self.typeText.setPlainText(type(self.node).__name__) self.titleText.setPlainText(self.node.title) titleHeight = int(self.titleText.boundingRect().height() / 20 + 0.5) * 20 self.idRect.setRect(x, y, 50, 20) self.typeRect.setRect(x + 50, y, 150, 20) self.titleRect.setRect(x, y + 20, 200, titleHeight) self.idRect.setBrush(QBrush(QColor(background))) self.typeRect.setBrush(QBrush(QColor(background))) self.titleRect.setBrush(QBrush(QColor(background))) self.idRect.setPen(QPen(QColor(border), 2)) self.typeRect.setPen(QPen(QColor(border), 2)) self.titleRect.setPen(QPen(QColor(border), 2)) self.idText.setPos(x, y - 2) self.typeText.setPos(x + self.typeOffset, y - 2) self.titleText.setPos(x, y + 18) self.headerHeight = titleHeight + 20 self.setToolTip(self.node.description) self.headerGroup.addToGroup(self.idRect) self.headerGroup.addToGroup(self.typeRect) self.headerGroup.addToGroup(self.titleRect) self.headerGroup.addToGroup(self.idText) self.headerGroup.addToGroup(self.typeText) self.headerGroup.addToGroup(self.titleText) self.addToGroup(self.headerGroup) def printAttributes(self, background, border, text): """ Prints the attributes of the node The attributes are a key, value pair :param background: background color of the node :param border: border color for the node :param text: text color for the node """ y = self.y() + self.headerHeight x = self.x() self.attributesHeight = 0 for k, v in self.node.attributes.items(): key = QGraphicsTextItem() key.setFont(Configuration.font) key.setDefaultTextColor(QColor(text)) key.setTextWidth(100) key.setPlainText(k) keyHeight = int(key.boundingRect().height() / 20 + 0.5) * 20 value = QGraphicsTextItem() value.setFont(Configuration.font) value.setDefaultTextColor(QColor(text)) value.setTextWidth(100) value.setPlainText(v) valueHeight = int(value.boundingRect().height() / 20 + 0.5) * 20 height = valueHeight if valueHeight > keyHeight else keyHeight keyRect = QGraphicsRectItem() keyRect.setRect(x, y, 100, height) valueRect = QGraphicsRectItem() valueRect.setRect(x + 100, y, 100, height) keyRect.setBrush(QBrush(QColor(background))) valueRect.setBrush(QBrush(QColor(background))) keyRect.setPen(QPen(QColor(border), 2)) valueRect.setPen(QPen(QColor(border), 2)) key.setPos(x, y - 2) value.setPos(x + 100, y - 2) self.attributes.addToGroup(keyRect) self.attributes.addToGroup(valueRect) self.attributes.addToGroup(key) self.attributes.addToGroup(value) y = y + height self.attributesHeight += height self.addToGroup(self.attributes) def redrawOptions(self, background, border, text): """ Redraws the node with option for the background, border and text color :param background: background color of the node :param border: border color for the node :param text: text color for the node """ y = self.y() x = self.x() self.prepareGeometryChange() for i in self.attributes.childItems(): self.attributes.removeFromGroup(i) self.parent.scene.removeItem(i) for i in self.headerGroup.childItems(): self.headerGroup.removeFromGroup(i) self.parent.scene.removeItem(i) for i in self.footerGroup.childItems(): self.footerGroup.removeFromGroup(i) self.parent.scene.removeItem(i) self.removeFromGroup(self.attributes) self.removeFromGroup(self.footerGroup) self.removeFromGroup(self.headerGroup) self.printHeader(background, border, text) self.printAttributes(background, border, text) self.printFooter(background, border, text) self.parent.scene.removeItem(self) self.parent.scene.addItem(self) self.setPos(x, y) def redraw(self): """ Redraws the node with standard colors """ self.redrawOptions(Qt.white, Qt.black, Qt.black) def printFooter(self, background, border, text): """ Prototype function for the footer. Implemented in the child classes :param background: background color of the node :param border: border color for the node :param text: text color for the node """ pass def setPos(self, x, y): """ Overloads setPos to set the position of the visible node in the data node :param x: X part of the position :param y: Y part of the position """ self.node.position = (x, y) super().setPos(x, y) def paint(self, painter, options, widget=None): """ Reimplementation for the paint function of the QGraphicsItemGroup. The Reimplementation is needed to print a proper border when the item is selected :param painter: The painter, which draws the node :param options: options for the paint job :param widget: widget of the Item """ myOption = QStyleOptionGraphicsItem(options) myOption.state &= ~QStyle.State_Selected super().paint(painter, myOption, widget=None) if options.state & QStyle.State_Selected: painter.setPen(QPen(Qt.black, 2, Qt.DotLine)) rect = QRect(self.boundingRect().x() - 1.5, self.boundingRect().y() - 1.5, self.boundingRect().x() + self.boundingRect().width() + 3, self.boundingRect().y() + self.boundingRect().height() + 0.5) painter.drawRect(rect) def selectChildren(self): """ Select all children """ self.setSelected(True) for i in self.childEdges: i.setSelected(True) i.dst.selectChildren() def delete(self): """ Deletes this node """ for e in self.parentEdges: self.parent.scene.removeItem(e) for e in self.childEdges: self.parent.scene.removeItem(e) self.parent.tree.removeNode(self.node.id) self.parent.scene.removeItem(self) def edit(self): """ Opens the edit dialog """ NodeEdit(self, self.parent).exec() def mouseDoubleClickEvent(self, event): """ Handles a double click on the node. The double click opens the edit window for this node :param event: click event """ self.edit() def dropEvent(self, event): """ Sets the correct position to the data node if the item is drag & dropped :param event: Drop event :return: Changed Value """ print("ok") self.node.position = (event.pos().x(), event.pos().y()) return super().dropEvent(self, event)
class Ui_MainWindow(QtWidgets.QMainWindow, Ui_MainWindowBase): def __init__(self): super(Ui_MainWindow, self).__init__() self.setupUi(self) self.videoPlaybackInit() self.imgInit() self.menuInit() self.df = None self.trackingPathGroup = None self.drawingFlag = False self.handInputSystem = None self.handInputSystem = HandInputSystem() self.handInputSystem.setRect(self.inputScene.sceneRect()) self.inputScene.addItem(self.handInputSystem) self.handInputSystem.addNewDataFrame() self.currentFrameNo = 0 self.colors = [] self.circleCheckBox.stateChanged.connect(self.polyLineCheckBoxStateChanged) self.lineCheckBox.stateChanged.connect(self.polyLineCheckBoxStateChanged) self.overlayCheckBox.stateChanged.connect(self.overlayCheckBoxStateChanged) self.radiusSpinBox.valueChanged.connect(self.radiusSpinBoxValueChanged) self.frameNoSpinBox.valueChanged.connect(self.frameNoSpinBoxValueChanged) self.groupBox_2.hide() self.inputGraphicsView.viewport().setCursor(QtCore.Qt.ArrowCursor) # self.optionViewButton.pressed.connect(self.optionViewButtonPressed) self.zoomedGraphicsView.hide() self.dataFrameWidget.dataFrameChanged.connect(self.dataFrameChanged) self.dataFrameWidget.hide() self.handInputSystem.setColor(self.dataFrameWidget.getColor()) #self.processDropedFile("/Users/ymnk/temp/Dast/2016/01/hoge.avi") #self.processDropedFile("./a.csv") def dataFrameChanged(self,addedFrameFlag,editingNo,color): if addedFrameFlag: self.handInputSystem.addNewDataFrame() self.handInputSystem.setEditingNo(editingNo) self.handInputSystem.setColor(color) print(addedFrameFlag,color,editingNo) def optionViewButtonPressed(self): if self.groupBox_2.isVisible(): self.optionViewButton.setText("<") self.groupBox_2.hide() else: self.optionViewButton.setText(">") self.groupBox_2.show() def overlayCheckBoxStateChanged(self, s): if self.overlayCheckBox.isChecked(): self.frameBufferItemGroup.show() else: self.frameBufferItemGroup.hide() self.updateInputGraphicsView() def polyLineCheckBoxStateChanged(self, s): overlayFrameNo = self.frameNoSpinBox.value() min_value = max(self.currentFrameNo-overlayFrameNo,0) current_pos = self.currentFrameNo - min_value if self.handInputSystem is not None: self.handInputSystem.setDrawItem(current_pos, self.circleCheckBox.isChecked()) self.handInputSystem.setDrawLine(self.lineCheckBox.isChecked()) self.updateInputGraphicsView() def radiusSpinBoxValueChanged(self, value): if self.handInputSystem is not None: self.handInputSystem.setRadius(self.radiusSpinBox.value()) self.updateInputGraphicsView() def frameNoSpinBoxValueChanged(self, value): if self.handInputSystem is not None: self.handInputSystem.setOverlayFrameNo(self.frameNoSpinBox.value()) self.updateInputGraphicsView() def dragEnterEvent(self,event): event.acceptProposedAction() def dropEvent(self,event): # event.setDropAction(QtCore.Qt.MoveAction) mime = event.mimeData() if mime.hasUrls(): urls = mime.urls() if len(urls) > 0: self.processDropedFile(urls[0].toLocalFile()) event.acceptProposedAction() def processDropedFile(self,filePath): root,ext = os.path.splitext(filePath) if ext == ".filter": # Read Filter self.openFilterFile(filePath=filePath) return elif ext == ".csv": self.openCSVFile(filePath=filePath) elif self.openImageFile(filePath=filePath): return elif self.openVideoFile(filePath=filePath): return def videoPlaybackInit(self): self.videoPlaybackWidget.hide() self.videoPlaybackWidget.frameChanged.connect(self.setFrame, Qt.QueuedConnection) def setFrame(self, frame, frameNo): if frame is not None: self.cv_img = frame self.currentFrameNo = frameNo self.updateInputGraphicsView() if self.videoInitialFlag == True: self.graphicsViewResized() self.videoInitialFlag = False self.evaluate() def imgInit(self): self.cv_img = cv2.imread(os.path.join(sampleDataPath,"color_filter_test.png")) self.frameBuffer = Queue() self.frameBufferItemGroup = QGraphicsItemGroup() self.frameBufferItemGroup.hide() self.inputPixmapRenderScene = QGraphicsScene() self.inputPixmapRenderScene.addItem(self.frameBufferItemGroup) self.inputScene = QGraphicsScene() self.inputGraphicsView.setScene(self.inputScene) self.inputGraphicsView.resizeEvent = self.graphicsViewResized # self.inputScene.addItem(self.frameBufferItemGroup) qimg = misc.cvMatToQImage(self.cv_img) self.inputPixmap = QPixmap.fromImage(qimg) self.inputPixmapItem = QGraphicsPixmapItem(self.inputPixmap) self.inputScene.addItem(self.inputPixmapItem) self.inputGraphicsView.mousePressEvent = self.inputGraphicsViewMousePressEvent self.inputGraphicsView.mouseMoveEvent = self.inputGraphicsViewMouseMoveEvent self.inputGraphicsView.mouseReleaseEvent = self.inputGraphicsViewMouseReleaseEvent self.inputGraphicsView.keyPressEvent = self.inputGraphicsViewKeyPressEvent self.inputGraphicsView.keyReleaseEvent = self.inputGraphicsViewKeyReleaseEvent self.inputGraphicsView.wheelEvent = self.inputGraphicsViewwheelEvent #self.inputGraphicsView.focusInEvent = self.inputGraphicsViewfocusInEvent self.inputGraphicsView.viewport().installEventFilter(self) self.inputGraphicsView.setMouseTracking(True) self.overlayScene = QGraphicsScene() self.inputGraphicsView.setOverlayScene(self.overlayScene) def inputGraphicsViewfocusInEvent(self,event): # QGraphicsView.focusInEvent(self.inputGraphicsView, event) def inputGraphicsViewMousePressEvent(self, event): if event.modifiers() == QtCore.Qt.ShiftModifier: # Comment out to permit the view for sending the event to the child scene. QGraphicsView.mousePressEvent(self.inputGraphicsView, event) else: self.drawingFlag = True if not self.videoPlaybackWidget.isPlaying(): self.videoPlaybackWidget.playButtonClicked() def inputGraphicsViewMouseMoveEvent(self, event): if event.modifiers() == QtCore.Qt.ShiftModifier: # Comment out to permit the view for sending the event to the child scene. QGraphicsView.mouseMoveEvent(self.inputGraphicsView, event) elif self.drawingFlag == True: pass #self.videoPlaybackWidget.moveNextButtonClicked() #self.handInputSystem.inputMouseMoveEvent(mousePosition,self.currentFrameNo) #print(self.currentFrameNo) #self.positionStack.append(mousePosition) #self.handInputSystem.inputMouseMoveEvent(mousePosition) def inputGraphicsViewMouseReleaseEvent(self, event): if self.drawingFlag == True: self.drawingFlag = False self.videoPlaybackWidget.playButtonClicked() self.handInputSystem.inputMouseReleaseEvent() self.handInputSystem.setPoints() # Comment out to permit the view for sending the event to the child scene. QGraphicsView.mouseReleaseEvent(self.inputGraphicsView, event) self.inputGraphicsView.viewport().setCursor(QtCore.Qt.ArrowCursor) def inputGraphicsViewKeyPressEvent(self,event): mousePosition = QCursor().pos() mousePosition = self.inputGraphicsView.mapFromGlobal(mousePosition) if event.type() == QtCore.QEvent.KeyPress: key = event.key() if key == QtCore.Qt.Key_Space: self.videoPlaybackWidget.playButtonClicked() elif key == QtCore.Qt.Key_A: self.videoPlaybackWidget.movePrevButtonClicked() elif key == QtCore.Qt.Key_D: self.videoPlaybackWidget.moveNextButtonClicked() elif key == QtCore.Qt.Key_Down: self.inputGraphicsViewScaleDown() elif key == QtCore.Qt.Key_Up: self.inputGraphicsViewScaleUp() pass elif key == QtCore.Qt.Key_R: self.graphicsViewResized() elif key == QtCore.Qt.Key_P: pass #self.handInputSystem.nextDataFrame() elif key == QtCore.Qt.Key_O: pass #self.handInputSystem.previousDataFrame() elif key == QtCore.Qt.Key_J: frameNo = self.handInputSystem.getLastInputedFrameIndex() self.videoPlaybackWidget.moveToFrame(frameNo) elif key == QtCore.Qt.Key_S: self.handInputSystem.saveCSV("./a.csv") QGraphicsView.keyPressEvent(self.inputGraphicsView, event) def inputGraphicsViewKeyReleaseEvent(self,event): QGraphicsView.keyReleaseEvent(self.inputGraphicsView, event) def inputGraphicsViewwheelEvent(self,event): scaleFactor = 1.15 if event.delta() > 0: # Zoom in self.inputGraphicsView.scale(scaleFactor, scaleFactor) else: # Zooming out self.inputGraphicsView.scale(1.0 / scaleFactor, 1.0 / scaleFactor) QGraphicsView.wheelEvent(self.inputGraphicsView, event) def inputGraphicsViewScaleDown(self): scaleFactor = 1.15 self.inputGraphicsView.scale(1.0 / scaleFactor, 1.0 / scaleFactor) def inputGraphicsViewScaleUp(self): scaleFactor = 1.15 self.inputGraphicsView.scale(scaleFactor, scaleFactor) def menuInit(self): self.actionSaveCSVFile.triggered.connect(self.saveCSVFile) self.actionOpenCSVFile.triggered.connect(self.openCSVFile) def openVideoFile(self, activated=False, filePath = None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Video File', userDir) if len(filePath) is not 0: self.filePath = filePath ret = self.videoPlaybackWidget.openVideo(filePath) if ret == False: return False self.videoInitialFlag = True self.videoPlaybackWidget.show() self.dataFrameWidget.show() # self.evaluate() return True else: return False def openImageFile(self, activated=False, filePath = None): if filePath == None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open Image File', userDir) if len(filePath) is not 0: self.filePath = filePath img = cv2.imread(filePath) if img is None: return False self.cv_img = img self.videoPlaybackWidget.hide() self.updateInputGraphicsView() self.evaluate() return True else: return False def openCSVFile(self, activated=False, filePath=None): if filePath is None: filePath, _ = QFileDialog.getOpenFileName(None, 'Open CSV File', userDir, 'CSV files (*.csv)') if len(filePath) is not 0: self.df = pd.read_csv(filePath, index_col=0) if self.handInputSystem is not None: self.inputScene.removeItem(self.handInputSystem) self.handInputSystem = HandInputSystem() self.handInputSystem.setRect(self.inputScene.sceneRect()) self.inputScene.addItem(self.handInputSystem) self.handInputSystem.setDataFrame(self.df) self.handInputSystem.setPoints() self.dataFrameWidget.clear() self.dataFrameWidget.dataFrameNo = self.handInputSystem.dataFrameNo self.dataFrameWidget.editingNo = 0 for item in range(self.handInputSystem.dataFrameNo+1): color = self.handInputSystem.itemList[item].getColor() print(item,color) self.dataFrameWidget.colorList.append(color) self.dataFrameWidget.setUniqueIDLabel() self.evaluate() def saveCSVFile(self, activated=False, filePath = None): #if self.df is not None: if self.handInputSystem.isDataFrame(): filePath, _ = QFileDialog.getSaveFileName(None, 'Save CSV File', userDir, "CSV files (*.csv)") if len(filePath) is not 0: logger.debug("Saving CSV file: {0}".format(filePath)) self.handInputSystem.saveCSV(filePath) def updateInputGraphicsView(self): # print("update") # self.inputScene.clear() self.inputScene.removeItem(self.inputPixmapItem) qimg = misc.cvMatToQImage(self.cv_img) self.inputPixmap = QPixmap.fromImage(qimg) p = QPainter(self.inputPixmap) sourceRect = self.inputPixmapRenderScene.sceneRect() self.inputPixmapRenderScene.render(p, QRectF(sourceRect), QRectF(sourceRect), QtCore.Qt.IgnoreAspectRatio) self.inputPixmapItem = QGraphicsPixmapItem(self.inputPixmap) rect = QtCore.QRectF(self.inputPixmap.rect()) self.inputScene.setSceneRect(rect) self.inputScene.addItem(self.inputPixmapItem) self.inputGraphicsView.viewport().update() # self.graphicsViewResized() def eventFilter(self, obj, event): if obj is self.inputGraphicsView.viewport() and event.type()==QEvent.Wheel: return True else: return False def graphicsViewResized(self, event=None): # print("resize") # print(self.inputScene) self.inputGraphicsView.fitInView(QtCore.QRectF(self.inputPixmap.rect()), QtCore.Qt.KeepAspectRatio) def evaluate(self): if not self.videoPlaybackWidget.isOpened(): return qimg = misc.cvMatToQImage(self.cv_img) pixmapItem = QGraphicsPixmapItem(QPixmap.fromImage(qimg)) pixmapItem.setOpacity(0.2) self.frameBuffer.put(pixmapItem) self.frameBufferItemGroup.addToGroup(pixmapItem) if self.frameBuffer.qsize() > 10: item = self.frameBuffer.get() self.frameBufferItemGroup.removeFromGroup(item) """ if self.trackingPathGroup is not None: self.trackingPathGroup.setPoints(self.currentFrameNo) """ if self.handInputSystem is not None: self.handInputSystem.setPoints(self.currentFrameNo) if self.drawingFlag is True: mousePosition = QCursor().pos() mousePosition = self.inputGraphicsView.mapFromGlobal(mousePosition) mousePosition = self.inputGraphicsView.mapToScene(mousePosition) pos = [mousePosition.x(),mousePosition.y()] self.handInputSystem.appendPosition(pos,self.currentFrameNo)
class MapWidget(QDialog): def __init__(self): QDialog.__init__(self) self.__mapWidgetUi = Ui_MapWidget() self.__mapWidgetUi.setupUi(self) self.__view = self.__mapWidgetUi.graphicsView self.__view.setObjectName("ACS_Map_View") self.__scene = QGraphicsScene() self.__view.setScene(self.__scene) self.__current_lat = 35.720428 self.__current_lon = -120.769924 self.__current_ground_width = 41000000. #meters(start w/ view of whole earth) #TODO: don't hard code location self.__tiler = acs_map_tiler.ACS_MapTiler(35.720428, -120.769924) self.__current_detail_layer = 0 #corresponds to "zoom" in map_tiler module self.__detail_layers = [] self.__rect_tiles = OrderedDict() #see rectKey method for how key works #detail layers are various levels of raster detail for the map. #0 is lowest level of detail, 20 highest. 0 loads fast and the #entire world can fit on a single tile. 20 loads slow, and it is unwise #to try to show the entire world due to the number of tiles required #(higher numbered detail layers are intended for "zooming in") self.setupDetailLayers() self.__plane_layer = QGraphicsItemGroup() self.__scene.addItem(self.__plane_layer) self.__plane_icons = {} self.__plane_labels = {} self.__plane_icon_pixmaps = {} img_bytes = pkg_resources.resource_stream("acs_dashboards", "data/images/flyingWingTiny.png").read() self.__plane_icon_pixmaps[0] = QPixmap() self.__plane_icon_pixmaps[0].loadFromData(img_bytes) img_bytes2 = pkg_resources.resource_stream("acs_dashboards", "data/images/flyingWingTiny2.png").read() self.__plane_icon_pixmaps[1] = QPixmap() self.__plane_icon_pixmaps[1].loadFromData(img_bytes2) #for drawing waypoints: self.__mission_layer = QGraphicsItemGroup() self.__scene.addItem(self.__mission_layer) self.__wp_diameter = 0.0001 self.__prev_drawn_nav_wp = None #needed when drawing lines between wps self.__wp_loiter_radius = None #NOTE: this is in meters; the map itself #is in degrees #for drawing fence: self.__fence_layer = QGraphicsItemGroup() self.__scene.addItem(self.__fence_layer) #slots self.__view.just_zoomed.connect(self.onZoom) self.__mapWidgetUi.zoom_sb.valueChanged.connect(self.onZoomSBValueChanged) self.__view.just_panned.connect(self.onPan) def getView(self): return self.__view def rectKey(self, x, y): '''rect_tiles key''' return (self.__current_detail_layer, x, y) #returns (min_lat, max_lat, min_lon, max_lon) as a tuple def extentsOfVisibleWorld(self): topLeft = self.__view.mapToScene(0,0) bottomRight = self.__view.mapToScene(self.__view.width(), self.__view.height()) return (-topLeft.y(), -bottomRight.y(), topLeft.x(), bottomRight.x()) #returns a list of TileInfos that are currently visible def tilesInVisibleWorld(self): (latTop, latBottom, lonLeft, lonRight) = self.extentsOfVisibleWorld() #print("Extents:", latTop, latBottom, lonLeft, lonRight, "\n") tile_info_list = self.__tiler.area_to_tile_list_lat_lon(latTop, latBottom, lonLeft, lonRight, self.__current_detail_layer) return tile_info_list def setupDetailLayers(self): #setup detail layers 0-20 for i in range(0,21): self.__detail_layers.append(QGraphicsItemGroup()) #add layer to scene: self.__scene.addItem(self.__detail_layers[i]) #hide all detail layers until it's time to show one: self.__detail_layers[i].hide() #show only the current detail layer self.setCurrentDetailLayer(0) def setCurrentDetailLayer(self, layerNum): self.__detail_layers[self.__current_detail_layer].hide() self.__detail_layers[layerNum].show() self.__current_detail_layer = layerNum self.__tiler.set_max_zoom(self.__current_detail_layer+1) #self.__tiler.prefetch() self.addTilesToCurrentDetailLayer() def createRectFromTileInfo(self, tile_info): #if Rectangle already exists, don't create it again key = self.rectKey(tile_info.x, tile_info.y) if key in self.__rect_tiles: return self.__rect_tiles[key] (y, x) = tile_info.coord() #TODO: do something about the hard coded 256s (end_y, end_x) = tile_info.coord((256, 256)) #y values need to reflect across the equator due to the origin in scene space #being on the top right (as opposed to bottom left in tile space) y = -y end_y = -end_y #keep things simple at the edges of the map: if y < -85.0: y = -85.0 if end_y > 85.0: end_y = 85.0 width = end_x - x height = end_y - y #create rectangle for the TileInfo and put it into the scene self.__rect_tiles[key] = QGraphicsRectItem(x, y, width, height, self.__detail_layers[self.__current_detail_layer]) #add raster data to the rect tile self.__tiler.load_tile(tile_info) #no border self.__rect_tiles[key].setPen(QPen(Qt.NoPen)) #remember the tiling data self.__rect_tiles[key].setData(0, tile_info) #attempt to add tile texture to rectangle: self.textureRect(self.__rect_tiles[key]) return self.__rect_tiles[key] def addTilesToCurrentDetailLayer(self): tile_info_list = self.tilesInVisibleWorld() for next_tile_info in tile_info_list: next_rect = self.createRectFromTileInfo(next_tile_info) #add rect tile to appropriate detail layer self.__detail_layers[self.__current_detail_layer].addToGroup(next_rect) #returns True if texture successfully applied, False otherwise #depth applies to how many zoom levels (or detail layers) we have traveled #while attempting to get a lower resolution texture def textureRect(self, rect_tile): tile_info = rect_tile.data(0) if tile_info is None: return False pm = QPixmap(self.__tiler.tile_to_path(tile_info)) if pm.width() != 256: #print("Probably didn't get tile:", next_tile_info.x, next_tile_info.y, "\n") #TODO: Attempt to texture with a lower res tile #Bear in mind that you will have to take Mercator projection into #account on the lower res tile. #First Attempt: didn't work #if tile_info.zoom <= self.__tiler.get_min_zoom(): # return False # #find colocated lower res tile #(lat,lon) = tile_info.coord() #tile_info2 = self.__tiler.coord_to_tile(lat,lon, tile_info.zoom-1) #rect_tile.setData(0, tile_info2) #print("prev tile: ", tile_info.tile, tile_info.coord()) #return self.textureRect(rect_tile, depth + 1) #until such time as we can pull lower res tiles and figure out #which area to render on a rectangle, skip: return False topLeft = rect_tile.boundingRect().topLeft() bottomRight = rect_tile.boundingRect().bottomRight() width = bottomRight.x() - topLeft.x() height = bottomRight.y() - topLeft.y() brush_trans = QTransform() brush_trans.translate(topLeft.x(), topLeft.y()) brush_trans.scale(width/256.0, height/256.0) qb = QBrush(pm) qb.setTransform(brush_trans) rect_tile.setBrush(qb) return True def checkForNewTextures(self): #ONLY care about rects in the current view: tile_info_list = self.tilesInVisibleWorld() for next_tile_info in tile_info_list: key = self.rectKey(next_tile_info.x, next_tile_info.y) # make sure key exists in self.__rect_tiles if key not in self.__rect_tiles: self.createRectFromTileInfo(next_tile_info) if self.__rect_tiles[key].brush().texture().width() != 256: self.textureRect(self.__rect_tiles[key]) #TODO: this code becomes applicable when lower res tiles become #available to higher zoom levels. #if self.__rect_tiles[key].data(0).zoom < self.__current_detail_layer: #lower res in there, try to get higher res #(remove tile info of the lower res tile): # del self.__rect_tiles[key] def clearFencePoints(self): children = self.__fence_layer.childItems() for child in children: self.__fence_layer.removeFromGroup(child) def clearWPs(self): children = self.__mission_layer.childItems() for child in children: self.__mission_layer.removeFromGroup(child) self.__prev_drawn_nav_wp = None def addWP(self, wp): if wp.command in [mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, mavutil.mavlink.MAV_CMD_NAV_LOITER_TO_ALT, mavutil.mavlink.MAV_CMD_NAV_LOITER_TURNS, mavutil.mavlink.MAV_CMD_NAV_LOITER_TIME, mavutil.mavlink.MAV_CMD_NAV_LOITER_UNLIM, mavutil.mavlink.MAV_CMD_NAV_LAND]: #point rad = self.__wp_diameter * 0.5 ellipse = QGraphicsEllipseItem(wp.y - rad, -wp.x - rad, self.__wp_diameter, self.__wp_diameter, self.__mission_layer) ellipse.setBrush(QBrush(QColor(255, 255, 255))) e_pen = QPen(QColor(255, 255, 255)) e_pen.setWidth(0) ellipse.setPen(e_pen) self.__mission_layer.addToGroup(ellipse) #label label = QGraphicsTextItem(str(wp.seq), self.__mission_layer) label.setZValue(2) label.setDefaultTextColor(Qt.white) label.setPos(wp.y + rad, -wp.x - rad) label.setScale(0.00002) #bit hacky -- really should scale based on #current zoom, but I'm in a hurry. self.__mission_layer.addToGroup(label) label.show() #argument is a dictionary of waypoints (keys = ids) def drawNewMission(self, wps): self.clearWPs() self.drawMissionLines(wps) def drawWPLine(self, wp1, wp2): if wp1 is None or wp2 is None: print("Error: Can't draw line for Null endpoint") return rad = self.__wp_diameter * 0.5 mapped_wp1_x = wp1.y - rad mapped_wp1_y = -wp1.x #tangential approach? if (wp1.command == mavutil.mavlink.MAV_CMD_NAV_LOITER_TO_ALT and wp1.param1 == 1.0): l_rad = abs(self.__wp_loiter_radius) if l_rad is not None: dis = acs_math.gps_distance(wp1.x, wp1.y, wp2.x, wp2.y) theta = math.degrees(math.atan(l_rad / dis)) #sign of self.__wp_loiter_radius indicates the direction of the #loiter (negative is counter-clockwise, postive is clockwise) #Also, the waypoint itself can override the default setting #via param2 if ((wp1.param2 == 0 and self.__wp_loiter_radius > 0.0) or wp1.param2 > 0.0): theta = -theta tan_dis = math.sqrt( dis * dis - l_rad * l_rad) bearing = acs_math.gps_bearing(wp2.x, wp2.y, wp1.x, wp1.y) (tan_wp_x, tan_wp_y) = acs_math.gps_newpos(wp2.x, wp2.y, bearing - theta, tan_dis) mapped_wp1_x = tan_wp_y - rad mapped_wp1_y = -tan_wp_x next_line = QGraphicsLineItem(mapped_wp1_x, mapped_wp1_y, wp2.y - rad, -wp2.x, self.__mission_layer) l_pen = QPen(QColor(255, 255, 255)) l_pen.setWidth(0.00002) next_line.setPen(l_pen) self.__mission_layer.addToGroup(next_line) def setWPLoiterRad(self, rad): self.__wp_loiter_radius = rad def drawWPCircle(self, wp): if self.__wp_loiter_radius is None: print("Can't draw WP loiter circles; wp_loiter_radius not set.") return rad_deg = acs_math.gps_meters_to_degrees_lat(abs(self.__wp_loiter_radius), wp.x, wp.y) diameter = rad_deg * 2.0 ellipse = QGraphicsEllipseItem(wp.y - rad_deg, -wp.x - rad_deg, diameter, diameter, self.__mission_layer) #ellipse.setBrush(QBrush(QColor(255, 255, 255))) e_pen = QPen(QColor(255, 255, 255)) e_pen.setWidth(0.00002) ellipse.setPen(e_pen) self.__mission_layer.addToGroup(ellipse) def drawMissionLines(self, wps): for i in range(1, len(wps)): self.addWP(wps[i]) if wps[i].command in [mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, mavutil.mavlink.MAV_CMD_NAV_LOITER_TO_ALT, mavutil.mavlink.MAV_CMD_NAV_LOITER_TURNS, mavutil.mavlink.MAV_CMD_NAV_LOITER_TIME, mavutil.mavlink.MAV_CMD_NAV_LOITER_UNLIM]: if self.__prev_drawn_nav_wp is not None: self.drawWPLine(self.__prev_drawn_nav_wp, wps[i]) if wps[i].command in [mavutil.mavlink.MAV_CMD_NAV_LOITER_UNLIM, mavutil.mavlink.MAV_CMD_NAV_LOITER_TURNS, mavutil.mavlink.MAV_CMD_NAV_LOITER_TIME, mavutil.mavlink.MAV_CMD_NAV_LOITER_TO_ALT]: self.drawWPCircle(wps[i]) self.__prev_drawn_nav_wp = wps[i] elif wps[i].command == mavutil.mavlink.MAV_CMD_DO_JUMP: wp_id = wps[i].param1 if self.__prev_drawn_nav_wp is not None: self.drawWPLine(self.__prev_drawn_nav_wp, wps[wp_id]) self.__prev_drawn_nav_wp = None def drawNewFence(self, fps): self.clearFencePoints() prev_fp = None for i in range(1, len(fps)): if prev_fp is not None: prev_x = prev_fp.lon prev_y = -prev_fp.lat mapped_x = fps[i].lon mapped_y = -fps[i].lat next_line = QGraphicsLineItem(prev_x, prev_y, mapped_x, mapped_y, self.__fence_layer) l_pen = QPen(QColor(0, 255, 0)) l_pen.setWidth(0.1) next_line.setPen(l_pen) self.__fence_layer.addToGroup(next_line) prev_fp = fps[i] def updateIcon(self, id, uav_state, image_num=0): if uav_state.get_lon() == 0.0: #haven't received a Pose message yet return if id not in self.__plane_icons: #make the plane's label first label = QGraphicsTextItem(str(id), self.__plane_layer) label.setZValue(2) label.setDefaultTextColor(Qt.red) self.__plane_layer.addToGroup(label) label.show() self.__plane_labels[id] = label self.__plane_icons[id] = MapGraphicsIcon(id, label, self.__plane_layer) self.__plane_icons[id].centerIconAt(uav_state.get_lon(), -uav_state.get_lat()) self.__plane_icons[id].textureIcon(self.__plane_icon_pixmaps[image_num]) #plane icons need to be drawn on top of map tiles: self.__plane_icons[id].setZValue(1) self.__plane_layer.addToGroup(self.__plane_icons[id]) #key 0 = last update time self.__plane_icons[id].setData(0, 0) #refresh: #HACK: don't know why zooming works to refresh. Couldn't #get scene.invalidate() nor scene.update() to work self.onZoom(self.__current_detail_layer) #end if now = time.time() #if we don't have new pose data, then we don't update the plane icon if (self.__plane_icons[id].data(0) is None or self.__plane_icons[id].data(0) >= uav_state.get_last_pose_update()): return #place icon self.__plane_icons[id].centerIconAt(uav_state.get_lon(), -uav_state.get_lat()) #give correct heading: quat = uav_state.get_quat() heading = acs_math.yaw_from_quat(quat[0], quat[1], quat[2], quat[3]) self.__plane_icons[id].setHeading(heading) #set last update time self.__plane_icons[id].setData(0, now) def zoomTo(self, zoomLevel, lat = 0, lon = 0): self.__view.zoomTo(zoomLevel, lat, lon) def onZoom(self, zoom_level): self.setCurrentDetailLayer(zoom_level) self.__mapWidgetUi.zoom_sb.setValue(zoom_level) for id, nextPlaneIcon in self.__plane_icons.items(): nextPlaneIcon.scaleByViewAndTexture(self.__view) def onZoomSBValueChanged(self, new_zoom): if (int(self.__view.getCurrentZoom()) != new_zoom): self.__view.zoomTo(new_zoom) def onPan(self, new_lat, new_lon): lat_lon_str = str(new_lat) + ", " + str(new_lon) self.__mapWidgetUi.coords_lb.setText(lat_lon_str) def hideUAVIcon(self, id): if id in self.__plane_icons: self.__plane_icons[id].hide() self.__plane_labels[id].hide() def showUAVIcon(self, id): if id in self.__plane_icons: self.__plane_icons[id].show() self.__plane_labels[id].show()