예제 #1
0
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)
예제 #2
0
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)
예제 #3
0
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)
예제 #4
0
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)
예제 #5
0
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
예제 #6
0
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))
예제 #7
0
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
예제 #9
0
파일: nodeitems.py 프로젝트: bucaneer/flint
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())
예제 #10
0
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)
예제 #11
0
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)
예제 #12
0
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()