class VideoPlayerWithControls( QWidget ):
	def __init__( self ):
		super().__init__()

		self.video_w = QVideoWidget()
		self.v_player = QMediaPlayer( None, QMediaPlayer.VideoSurface )
		self.v_player.setVideoOutput( self.video_w )
		self.v_player.setNotifyInterval( 50 )
		self.v_player.stateChanged.connect( self.video_ended )

		self.controls = VideoControls( self.v_player )

		self._layout = QVBoxLayout()

		self._layout.addWidget( self.video_w )
		self._layout.addWidget( self.controls )

		self._layout.setSpacing( 0 )
		self._layout.setContentsMargins( 0, 0, 0, 0 )

		self.setLayout( self._layout )

	def video_ended( self, state ):
		if state == QMediaPlayer.StoppedState:
			self.v_player.setPosition( self.v_player.duration() - 10 )
			self.v_player.play()
			self.v_player.pause()
Exemple #2
0
class QMediaPlayerAdapter(Player):
    def __init__(self, interval=1):
        self.__callback = None
        self.__player = QMediaPlayer()
        self.setNotifyInterval(interval)

    def setNotifyInterval(self, interval):
        self.__player.setNotifyInterval(interval)

    def setMusicPath(self, filePath):
        qUrl = QUrl(filePath)
        self.__player.setMedia(QMediaContent(qUrl))

    def getState(self):
        return self.__player.state()

    def getStatus(self):
        return self.__player.mediaStatus()

    def getPosition(self):
        return self.__player.position()

    def getDuration(self):
        return self.__player.duration()

    def start(self):
        if self.__player.state() != QMediaPlayer.NoMedia:
            self.__player.play()
            # self.__player.setPosition(self.__player.duration() - 2000)
        else:
            raise ValueError(
                "The media of the player should be set before the player get started."
            )

    def pause(self):
        self.__player.pause()

    def stop(self):
        self.__player.stop()

    def setCallback(self, callback):
        '''if not isinstance(callback, PlayerCallback):
            raise TypeError("The callback should be instance of PlayerCallback.")'''

        self.__callback = callback
        self.__player.mediaStatusChanged.connect(
            self.__callback.onMediaStatusChanged)
        self.__player.stateChanged.connect(self.__callback.onStateChanged)
        self.__player.positionChanged.connect(
            self.__callback.onPlayerPositionChanged)
Exemple #3
0
class VideoWindow(QMainWindow):
    def __init__(self, parent=None):
        super(VideoWindow, self).__init__(parent)
        self.first = True

        # -추가 : 양팡관련
        self.YangPang_play = False
        self.YangPang_first = 0

        global player
        global channel_ls

        # 제목 표시줄
        self.setWindowTitle(
            "PyQt Video Player Widget Example - pythonprogramminglanguage.com")

        # 비디오 파일 재생 위젯 생성
        self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)

        # 비디오 출력 프레임 부분 위젯 생성
        self.videoWidget = QVideoWidget()

        # 플레이버튼 생성
        self.playButton = QPushButton()
        self.playButton.setEnabled(False)
        self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
        self.playButton.clicked.connect(self.play)

        # 플레이용 수평 슬라이드 바 생성
        self.positionSlider = QSlider(Qt.Horizontal)

        # 0부터 0까지 칸 수 만큼 슬라이드 할 수 있는 바 생성
        self.positionSlider.setRange(0, 0)
        self.positionSlider.sliderMoved.connect(self.setPosition)

        # - 추가 : 사운드 이미지 라벨 생성
        self.soundImage = QPushButton()
        self.soundImage.setIcon(self.style().standardIcon(
            QStyle.SP_MediaVolume))
        self.soundImage.clicked.connect(self.sound)

        # - 추가 : 사운드용 수평 슬라이드 바 생성
        self.soundpositionSlider = QSlider(Qt.Horizontal)
        self.soundpositionSlider.setSizePolicy(QSizePolicy.Preferred,
                                               QSizePolicy.Maximum)

        # - 추가 : 0부터 100까지 칸 수 만큼 슬라이드 할 수 있는 바 생성
        self.soundpositionSlider.setRange(0, 100)
        self.soundpositionSlider.setValue(100)
        self.soundpositionSlider.sliderMoved.connect(self.setsoundPosition)

        # - 추가 : 링크기능
        self.textLink = QLineEdit()
        self.buttonLink = QPushButton()
        self.textLink.setObjectName("link")
        self.buttonLink.setText("연결")
        self.buttonLink.clicked.connect(self.connect_video)
        self.buttonLink.setSizePolicy(QSizePolicy.Preferred,
                                      QSizePolicy.Maximum)

        # 에러 Label 생성
        self.errorLabel = QLabel()

        # sizepolicy 정리
        # http://mitk.org/images/5/5e/BugSquashingSeminars%2414-04-30-bugsquashing-Qt-Size-Policy.pdf
        # https://stackoverflow.com/questions/4553304/understanding-form-layout-mechanisms-in-qt
        self.errorLabel.setSizePolicy(QSizePolicy.Preferred,
                                      QSizePolicy.Maximum)

        # 메뉴의 open 생성
        openAction = QAction(QIcon('open.png'), '&Open', self)
        openAction.setShortcut('Ctrl+O')
        openAction.setStatusTip('Open movie')
        openAction.triggered.connect(self.openFile)

        # 메뉴의 exit 생성
        exitAction = QAction(QIcon('exit.png'), '&Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit application')
        exitAction.triggered.connect(self.exitCall)

        # - 추가 : 메뉴의 channel add 생성
        channelAction = QAction(QIcon('exit.png'), '&Channel', self)
        channelAction.setStatusTip('Youtube channel set')
        channelAction.triggered.connect(opensetwin)

        # 메뉴바 및 메뉴 생성
        menuBar = self.menuBar()
        fileMenu = menuBar.addMenu('&File')

        # 메뉴 목록 추가
        fileMenu.addAction(openAction)
        fileMenu.addAction(exitAction)
        # - 추가
        fileMenu.addAction(channelAction)

        # centralwidget 부분 추가
        wid = QWidget(self)
        self.setCentralWidget(wid)

        # -추가 : 양팡관련
        self.YB = QPushButton()
        self.YB.setText("양팡 Start")
        self.YB.clicked.connect(self.YangPang)

        # -추가 : 채널관련
        self.CL = QPushButton()
        self.CL.setText("채널 Start")
        self.CL.clicked.connect(self.ChannelVideo)

        # 레이아웃을 생성하고 위에서 만들었던 Widget 수평으로 순서대로 추가
        controlLayout = QHBoxLayout()
        controlLayout.addWidget(self.playButton)
        controlLayout.addWidget(self.positionSlider)
        controlLayout.addWidget(self.soundImage)
        controlLayout.addWidget(self.soundpositionSlider)

        # 링크 부분 레이아웃 생성
        linkLayout = QHBoxLayout()
        linkLayout.addWidget(self.textLink)
        linkLayout.addWidget(self.buttonLink)

        # controlLayout 바깥으로 여백 추가 (left, right, up, down)
        controlLayout.setContentsMargins(0, 0, 0, 0)

        # 레이아웃을 생성하고 이 래이아웃들을 수직 순서대로 추가
        layout = QVBoxLayout()
        layout.addWidget(self.videoWidget)
        layout.addLayout(controlLayout)
        layout.addLayout(linkLayout)

        # -추가 : 양팡관련
        layout.addWidget(self.YB)

        # -추가 : 채널관련
        layout.addWidget(self.CL)

        layout.addWidget(self.errorLabel)

        # CentralWidget에 layout 넣기
        wid.setLayout(layout)

        # 미디어 위젯에 비디오를 출력 시킴
        self.mediaPlayer.setVideoOutput(self.videoWidget)

        # 미디어 위젯의 플레이 상태가 바뀔 때
        self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)

        # 미디어 위젯의 슬라이드바의 위치가 바뀔 때
        self.mediaPlayer.positionChanged.connect(self.positionChanged)

        # 미디어 위젯의 영상이 바뀌어 슬라이드바의 총 나눔이 달라질 때
        self.mediaPlayer.durationChanged.connect(self.durationChanged)

        # 미디어 위젯에 오류가 났을 때
        self.mediaPlayer.error.connect(self.handleError)

        # - 추가 : 영상 1/3 나누어서 5초 앞 / 시작 or 정지 / 5초 뒤 투명한 버튼 생성
        hide_layout = QHBoxLayout()
        self.before = QPushButton()
        self.state_change = QPushButton()
        self.after = QPushButton()
        self.before.setEnabled(False)
        self.state_change.setEnabled(False)
        self.after.setEnabled(False)
        self.before.clicked.connect(self.before_f)
        self.state_change.clicked.connect(self.play)
        self.after.clicked.connect(self.after_f)
        self.before.setShortcut('left')
        self.state_change.setShortcut('space')
        self.after.setShortcut('right')
        hide_layout.addWidget(self.before)
        hide_layout.addWidget(self.state_change)
        hide_layout.addWidget(self.after)
        self.before.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.state_change.setSizePolicy(QSizePolicy.Preferred,
                                        QSizePolicy.Preferred)
        self.after.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.videoWidget.setLayout(hide_layout)

    # 파일 불러오기
    def openFile(self):
        # QFileDialog.getOpenFileName(self, "제목 표시줄", QDir.homePath())
        # 이 함수를 통해 filename엔 파일의 전체 경로와 어떤 종류로 가져 왔는지 변수로 줌(ex. All Files (*))
        fileName, _ = QFileDialog.getOpenFileName(self, "Open Movie",
                                                  QDir.homePath())
        # -추가 : 양팡관련
        self.YangPang_play = False
        self.YangPang_first = 0
        # 위에서 받아온 경로의 파일을 미디어위젯에 대입
        if fileName != '':
            self.mediaPlayer.setMedia(
                QMediaContent(QUrl.fromLocalFile(fileName)))
            self.playButton.setEnabled(True)
            self.check_url = False
            self.best = None

    # 윈도우 종료 함수
    def exitCall(self):
        sys.exit(app.exec_())

    # - 추가 : 링크로 연결 (유튜브 주소만 호환)
    def connect_video(self):
        # -추가 : 양팡관련
        if not self.YangPang_play:
            self.textValueB = self.textLink.text()
            self.YangPang_first = 0
        url = self.textValueB
        if self.textLink.text() != "":
            self.YangPang_play = False

        play_link = pafy.new(url)
        self.best = play_link.getbest()
        self.mediaPlayer.setMedia(QMediaContent(QUrl(self.best.url)))
        self.playButton.setEnabled(True)
        self.check_url = True
        self.play()

    # 플레이 버튼 함수
    def play(self):
        # 현재 미디어 위젯의 플레이 상태가 재생중일 때이거나 다른 경우
        # PlayingState는 Q미디어플레이어.play() 인듯 = 1
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()
        else:
            self.mediaPlayer.play()

    # - 추가 : 사운드 버튼 함수
    def sound(self):
        if self.mediaPlayer.isMuted():
            self.mediaPlayer.setMuted(False)
            self.soundImage.setIcon(self.style().standardIcon(
                QStyle.SP_MediaVolume))
        else:
            self.mediaPlayer.setMuted(True)
            self.soundImage.setIcon(self.style().standardIcon(
                QStyle.SP_MediaVolumeMuted))

    # 플레이 버튼 모양 변경
    def mediaStateChanged(self, state):
        # 현재 미디어 위젯의 플레이 상태가 재생중일 때이거나 다른 경우
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.playButton.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPause))
        else:
            self.playButton.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPlay))

        # 양팡 및 채널 관련
        if self.mediaPlayer.state() == QMediaPlayer.StoppedState:
            if self.YangPang_play:
                if self.YangPang_first == 1:
                    self.YangPang()
                if self.YangPang_first == 2:
                    self.ChannelVideo()

    # 영상의 진행 정도에 따른 슬라이드 바 위치 변경
    def positionChanged(self, position):
        self.positionSlider.setValue(position)
        self.position = position

    # 영상의 길이만큼 슬라이드 바의 최대값 설정
    def durationChanged(self, duration):
        self.positionSlider.setRange(0, duration)

        # - 추가 : 슬라이드 이동 간격 설정
        self.mediaPlayer.setNotifyInterval(1)

        # - 추가 : 자동으로 영상크기만큼 윈도우 크기 조절
        if self.mediaPlayer.metaData("Resolution") != None:
            if not self.check_url:
                player.resize(self.mediaPlayer.metaData("Resolution"))
        else:
            if self.best != None:
                x, y = parse("{}x{}", self.best.resolution)
                player.resize(int(x), int(y))

        # - 추가 : 영상 추가시 버튼 활성화
        self.before.setEnabled(True)
        self.state_change.setEnabled(True)
        self.after.setEnabled(True)

    # - 추가 : 5초 앞
    def before_f(self):
        self.mediaPlayer.setPosition(self.position - 5000)

    # - 추가 : 5초 뒤
    def after_f(self):
        self.mediaPlayer.setPosition(self.position + 5000)

    # 영상 슬라이드 바 위치에 따른 화면 변경
    def setPosition(self, position):
        self.mediaPlayer.setPosition(position)

    # 에러 출력 함수
    def handleError(self):
        self.playButton.setEnabled(False)
        self.before.setEnabled(False)
        self.state_change.setEnabled(False)
        self.after.setEnabled(False)
        self.errorLabel.setText("Error: " + self.mediaPlayer.errorString())

    # - 추가 : 사운드 슬라이드 바 위치에 따른 소리 크기 변경
    def setsoundPosition(self, position):
        self.mediaPlayer.setVolume(position)

    # - 추가 : 양팡 유튜브 영상 재생
    def YangPang(self):
        # if not self.YangPang_play:
        if self.YangPang_first == 1:
            web_url = self.textValueB

            with urllib.request.urlopen(web_url) as response:
                html = response.read()
                soup = BeautifulSoup(html, 'html.parser')
                yp_find = soup.find_all("a")

            self.yp_find_list = list()
            for info in yp_find:
                if parse("/watch?v={}", info["href"]) != None:
                    if str(info.find("span", {"class": "stat attribution"
                                              })).count('양팡 YangPang') > 0:
                        self.yp_find_list.append(info["href"])
                        break

        if self.YangPang_first != 1:
            web_url = "https://www.youtube.com/channel/UCMVC92EOs9yDJG5JS-CMesQ/videos"
            self.YangPang_first = 1

            with urllib.request.urlopen(web_url) as response:
                html = response.read()
                soup = BeautifulSoup(html, 'html.parser')
                yp_find = soup.find_all("a")

            self.yp_find_list = list()
            for info in yp_find:
                if parse("/watch?v={}", info["href"]) != None:
                    self.yp_find_list.append(info["href"])

        self.YangPang_play = True
        cnt = len(self.yp_find_list) - 1

        self.textValueB = "https://www.youtube.com/" + str(
            self.yp_find_list[random.randint(0, cnt)])
        self.setWindowTitle("양팡플레이어")
        self.connect_video()

    def ChannelVideo(self):
        if self.YangPang_first == 2:
            web_url = self.textValueB

            with urllib.request.urlopen(web_url) as response:
                html = response.read()
                soup = BeautifulSoup(html, 'html.parser')
                yp_find = soup.find_all("a")

            self.yp_find_list = list()
            for info in yp_find:
                if parse("/watch?v={}", info["href"]) != None:
                    if str(info.find("span", {"class": "stat attribution"
                                              })).count(self.name) > 0:
                        self.yp_find_list.append(info["href"])
                        break

            k = random.randint(0, 1)
            if k == 0:
                self.YangPang_first = 0

        if self.YangPang_first != 2:
            web_url = channel_ls[random.randint(0, len(channel_ls) - 1)]
            self.YangPang_first = 2

            with urllib.request.urlopen(web_url) as response:
                html = response.read()
                soup = BeautifulSoup(html, 'html.parser')
                yp_find = soup.find_all("a")
                self.name_ls = soup.find_all("img")

            for info in self.name_ls:
                if info["alt"] != None:
                    self.name = info["alt"]
                    break

            self.yp_find_list = list()
            for info in yp_find:
                if parse("/watch?v={}", info["href"]) != None:
                    self.yp_find_list.append(info["href"])

        self.YangPang_play = True
        cnt = len(self.yp_find_list) - 1

        self.textValueB = "https://www.youtube.com/" + str(
            self.yp_find_list[random.randint(0, cnt)])
        self.setWindowTitle("랜덤플레이어")
        self.connect_video()
Exemple #4
0
class QgsFmvPlayer(QMainWindow, Ui_PlayerWindow):
    """ Video Player Class """
    def __init__(self,
                 iface,
                 path,
                 parent=None,
                 meta_reader=None,
                 pass_time=None,
                 initialPt=None,
                 isStreaming=False):
        """ Constructor """
        super(QgsFmvPlayer, self).__init__(parent)
        self.setupUi(self)
        self.parent = parent
        self.iface = iface
        self.fileName = path
        self.initialPt = initialPt
        self.meta_reader = meta_reader
        self.isStreaming = isStreaming
        self.createingMosaic = False
        self.currentInfo = 0.0
        self.data = None

        # Create Draw Toolbar
        self.DrawToolBar.addAction(self.actionMagnifying_glass)
        self.DrawToolBar.addSeparator()

        # Draw Polygon QToolButton
        self.toolBtn_DPolygon.setDefaultAction(self.actionDraw_Polygon)
        self.DrawToolBar.addWidget(self.toolBtn_DPolygon)

        # Draw Point QToolButton
        self.toolBtn_DPoint.setDefaultAction(self.actionDraw_Pinpoint)
        self.DrawToolBar.addWidget(self.toolBtn_DPoint)

        # Draw Point QToolButton
        self.toolBtn_DLine.setDefaultAction(self.actionDraw_Line)
        self.DrawToolBar.addWidget(self.toolBtn_DLine)

        self.DrawToolBar.addAction(self.actionRuler)
        self.DrawToolBar.addSeparator()

        #         # Censure QToolButton
        #         self.toolBtn_Cesure.setDefaultAction(self.actionCensure)
        #         self.DrawToolBar.addWidget(self.toolBtn_Cesure)
        #         self.DrawToolBar.addSeparator()
        #
        #         # Object Tracking
        #         self.DrawToolBar.addAction(self.actionObject_Tracking)
        self.toolBtn_Cesure.setVisible(False)
        # Hide Color Button
        self.btn_Color.hide()

        self.RecGIF = QMovie(":/imgFMV/images/record.gif")

        self.videoWidget.customContextMenuRequested[QPoint].connect(
            self.contextMenuRequested)

        self.menubarwidget.customContextMenuRequested[QPoint].connect(
            self.contextMenuBarRequested)

        self.duration = 0
        self.playerMuted = False
        self.HasFileAudio = False

        self.player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.pass_time = pass_time
        self.player.setNotifyInterval(700)  # Metadata Callback Interval
        self.playlist = QMediaPlaylist()

        self.player.setVideoOutput(
            self.videoWidget.videoSurface())  # Abstract Surface

        self.player.durationChanged.connect(self.durationChanged)
        self.player.positionChanged.connect(self.positionChanged)
        self.player.mediaStatusChanged.connect(self.statusChanged)

        self.player.stateChanged.connect(self.setCurrentState)

        self.playerState = QMediaPlayer.LoadingMedia
        self.playFile(path)

        self.sliderDuration.setRange(0, self.player.duration() / 1000)

        self.volumeSlider.setValue(self.player.volume())
        self.volumeSlider.enterEvent = self.showVolumeTip

        self.metadataDlg = QgsFmvMetadata(parent=self, player=self)
        self.addDockWidget(Qt.RightDockWidgetArea, self.metadataDlg)
        self.metadataDlg.setMinimumWidth(500)
        self.metadataDlg.hide()

        self.converter = Converter()
        self.BitratePlot = CreatePlotsBitrate()

    def HasAudio(self, videoPath):
        """ Check if video have Metadata or not """
        try:
            p = _spawn([
                '-i', videoPath, '-show_streams', '-select_streams', 'a',
                '-preset', 'ultrafast', '-loglevel', 'error'
            ],
                       t="probe")

            stdout_data, _ = p.communicate()

            if stdout_data == b'':
                qgsu.showUserAndLogMessage(
                    QCoreApplication.translate(
                        "QgsFmvPlayer", "This video doesn't have Audio ! "))
                self.actionAudio.setEnabled(False)
                self.actionSave_Audio.setEnabled(False)
                return False

            return True
        except Exception as e:
            qgsu.showUserAndLogMessage(
                QCoreApplication.translate("QgsFmvPlayer",
                                           "Audio check Failed! : "), str(e))
            self.actionAudio.setEnabled(False)
            self.actionSave_Audio.setEnabled(False)

    def get_metadata_from_buffer(self, currentTime):
        """ Metadata CallBack """
        try:

            # There is no way to spawn a thread and call after join() without blocking the video UI thread.
            # callBackMetadata can be as fast as possible, it will always create a small video lag every time meta are read.
            # To get rid of this, we fill a buffer (BufferedMetaReader) in the QManager with some Metadata in advance,
            # and hope they'll be ready to read here in a totaly non-blocking
            # way (increase the buffer size if needed in QManager).

            stdout_data = self.meta_reader.get(currentTime)
            # qgsu.showUserAndLogMessage(
            #    "", "stdout_data: " + str(stdout_data) + " currentTime: " + str(currentTime), onlyLog=True)
            if stdout_data == 'NOT_READY':
                self.metadataDlg.menuSave.setEnabled(False)
                qgsu.showUserAndLogMessage(
                    "",
                    "Buffer value read but is not ready, increase buffer size. : ",
                    onlyLog=True)
                return

            #Values need to be read, pause the video a short while
            elif stdout_data == 'BUFFERING':
                qgsu.showUserAndLogMessage("Buffering metadata...",
                                           "",
                                           duration=4,
                                           level=QGis.Info)
                self.player.pause()
                QTimer.singleShot(2500, lambda: self.player.play())
                return

            elif stdout_data == b'' or len(stdout_data) == 0:
                self.metadataDlg.menuSave.setEnabled(False)
                qgsu.showUserAndLogMessage(
                    "",
                    "Buffer returned empty metadata, check pass_time. : ",
                    onlyLog=True)
                return

            self.packetStreamParser(stdout_data)

        except Exception as inst:
            qgsu.showUserAndLogMessage(
                QCoreApplication.translate("QgsFmvPlayer",
                                           "Metadata Buffer Failed! : "),
                str(inst))

    def packetStreamParser(self, stdout_data):
        ''' Common packet process'''
        for packet in StreamParser(stdout_data):
            try:
                if isinstance(packet, UnknownElement):
                    qgsu.showUserAndLogMessage(
                        "Error interpreting klv data, metadata cannot be read.",
                        "the parser did not recognize KLV data",
                        level=QGis.Warning,
                        onlyLog=True)
                    continue
                data = packet.MetadataList()
                self.data = data
                if self.metadataDlg.isVisible(
                ):  # Only add metada to table if this QDockWidget is visible (speed plugin)
                    self.metadataDlg.menuSave.setEnabled(True)
                    self.addMetadata(data)

                UpdateLayers(packet, parent=self, mosaic=self.createingMosaic)
                QApplication.processEvents()
                return
            except Exception:
                None
#                     qgsu.showUserAndLogMessage(QCoreApplication.translate(
#                         "QgsFmvPlayer", "Meta update failed! "), " Packet:" + str(packet) + ", error:" + str(inst), level=QGis.Warning)

    def callBackMetadata(self, currentTime, nextTime):
        """ Metadata CallBack """
        try:
            port = int(self.fileName.split(':')[2])
            t = callBackMetadataThread(cmds=[
                '-i',
                self.fileName.replace(str(port), str(
                    port + 1)), '-ss', currentTime, '-to', nextTime, '-map',
                'data-re', '-preset', 'ultrafast', '-f', 'data', '-'
            ])
            t.start()
            t.join(1)
            if t.is_alive():
                t.p.terminate()
                t.join()

            qgsu.showUserAndLogMessage("",
                                       "callBackMetadataThread self.stdout: " +
                                       str(t.stdout),
                                       onlyLog=True)

            if t.stdout == b'':
                return

            self.packetStreamParser(t.stdout)

        except Exception as e:
            qgsu.showUserAndLogMessage(
                QCoreApplication.translate("QgsFmvPlayer",
                                           "Metadata Callback Failed! : "),
                str(e))

    def GetPacketData(self):
        ''' Return Current Packet data '''
        return self.data

    def addMetadata(self, packet):
        ''' Add Metadata to List '''
        self.clearMetadata()
        row = 0
        for key in sorted(packet.keys()):
            self.metadataDlg.VManager.insertRow(row)
            self.metadataDlg.VManager.setItem(row, 0,
                                              QTableWidgetItem(str(key)))
            self.metadataDlg.VManager.setItem(
                row, 1, QTableWidgetItem(str(packet[key][0])))
            self.metadataDlg.VManager.setItem(
                row, 2, QTableWidgetItem(str(packet[key][1])))
            row += 1
        self.metadataDlg.VManager.setVisible(False)
        self.metadataDlg.VManager.resizeColumnsToContents()
        self.metadataDlg.VManager.setVisible(True)
        self.metadataDlg.VManager.verticalScrollBar().setSliderPosition(
            self.sliderPosition)

    def clearMetadata(self):
        ''' Clear Metadata List '''
        try:
            self.sliderPosition = self.metadataDlg.VManager.verticalScrollBar(
            ).sliderPosition()
            self.metadataDlg.VManager.setRowCount(0)
        except Exception:
            None

    def saveInfoToJson(self):
        """ Save video Info to json """
        out_json, _ = askForFiles(self,
                                  QCoreApplication.translate(
                                      "QgsFmvPlayer", "Save Json"),
                                  isSave=True,
                                  exts="json")

        if not out_json:
            return

        taskSaveInfoToJson = QgsTask.fromFunction(
            'Save Video Info to Json Task',
            self.converter.probeToJson,
            fname=self.fileName,
            output=out_json,
            on_finished=self.finishedTask,
            flags=QgsTask.CanCancel)

        QgsApplication.taskManager().addTask(taskSaveInfoToJson)
        return

    def showVideoInfo(self):
        ''' Show default probe info '''

        taskSaveInfoToJson = QgsTask.fromFunction(
            'Show Video Info Task',
            self.converter.probeShow,
            fname=self.fileName,
            on_finished=self.finishedTask,
            flags=QgsTask.CanCancel)

        QgsApplication.taskManager().addTask(taskSaveInfoToJson)
        return

    def state(self):
        ''' Return Current State '''
        return self.playerState

    def setCurrentState(self, state):
        ''' Set Current State '''
        if state != self.playerState:
            self.playerState = state
            if state == QMediaPlayer.StoppedState:
                self.btn_play.setIcon(QIcon(":/imgFMV/images/play-arrow.png"))

        return

    def showColorDialog(self):
        ''' Show Color dialog '''
        self.ColorDialog = ColorDialog(parent=self)
        self.ColorDialog.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint)
        # Fail if not uncheked
        self.actionMagnifying_glass.setChecked(False)
        self.ColorDialog.exec_()
        QApplication.processEvents()
        self.ColorDialog.contrastSlider.setValue(80)
        self.ColorDialog.contrastSlider.triggerAction(
            QAbstractSlider.SliderMove)
        return

    def createMosaic(self, value):
        ''' Function for create Video Mosaic '''
        home = os.path.expanduser("~")

        qgsu.createFolderByName(home, "QGIS_FMV")
        homefmv = os.path.join(home, "QGIS_FMV")
        root, _ = os.path.splitext(os.path.basename(self.fileName))
        qgsu.createFolderByName(homefmv, root)
        self.createingMosaic = value
        # Create Group
        CreateGroupByName()
        return

    def contextMenuBarRequested(self, point):
        ''' Context Menu Menu Bar '''
        menu = QMenu('ToolBars')
        toolbars = self.findChildren(QToolBar)
        for toolbar in toolbars:
            action = menu.addAction(toolbar.windowTitle())
            action.setCheckable(True)
            action.setChecked(toolbar.isVisible())
            action.setObjectName(toolbar.windowTitle())
            action.triggered.connect(lambda _: self.ToggleQToolBar())
        menu.exec_(self.mapToGlobal(point))
        return

    def ToggleQToolBar(self):
        ''' Toggle ToolBar '''
        toolbars = self.findChildren(QToolBar)
        for toolbar in toolbars:
            if self.sender().objectName() == toolbar.windowTitle():
                toolbar.toggleViewAction().trigger()

    def contextMenuRequested(self, point):
        ''' Context Menu Video '''
        menu = QMenu('Video')

        #         actionColors = menu.addAction(
        #             QCoreApplication.translate("QgsFmvPlayer", "Color Options"))
        #         actionColors.setShortcut("Ctrl+May+C")
        #         actionColors.triggered.connect(self.showColorDialog)

        actionMute = menu.addAction(
            QCoreApplication.translate("QgsFmvPlayer", "Mute/Unmute"))
        actionMute.setShortcut("Ctrl+Shift+U")
        actionMute.triggered.connect(self.setMuted)

        menu.addSeparator()
        actionAllFrames = menu.addAction(
            QCoreApplication.translate("QgsFmvPlayer", "Extract All Frames"))
        actionAllFrames.setShortcut("Ctrl+Shift+A")
        actionAllFrames.triggered.connect(self.ExtractAllFrames)

        actionCurrentFrames = menu.addAction(
            QCoreApplication.translate("QgsFmvPlayer",
                                       "Extract Current Frame"))
        actionCurrentFrames.setShortcut("Ctrl+Shift+Q")
        actionCurrentFrames.triggered.connect(self.ExtractCurrentFrame)

        menu.addSeparator()
        actionShowMetadata = menu.addAction(
            QCoreApplication.translate("QgsFmvPlayer", "Show Metadata"))
        actionShowMetadata.setShortcut("Ctrl+Shift+M")
        actionShowMetadata.triggered.connect(self.OpenQgsFmvMetadata)

        menu.exec_(self.mapToGlobal(point))

    # Start Snnipet FILTERS
    def grayFilter(self, value):
        ''' Gray Video Filter '''
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetGray(value)
        self.videoWidget.UpdateSurface()
        return

    def MirrorHorizontalFilter(self, value):
        ''' Mirror Horizontal Video Filter '''
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetMirrorH(value)
        self.videoWidget.UpdateSurface()
        return

    def edgeFilter(self, value):
        ''' Edge Detection Video Filter '''
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetEdgeDetection(value)
        self.videoWidget.UpdateSurface()
        return

    def invertColorFilter(self, value):
        ''' Invert Color Video Filter '''
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetInvertColor(value)
        self.videoWidget.UpdateSurface()
        return

    def autoContrastFilter(self, value):
        ''' Auto Contrast Video Filter '''
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetAutoContrastFilter(value)
        self.videoWidget.UpdateSurface()
        return

    def monoFilter(self, value):
        ''' Filter Mono Video '''
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetMonoFilter(value)
        self.videoWidget.UpdateSurface()
        return

    def magnifier(self, value):
        ''' Magnifier Glass Utils '''
        self.UncheckUtils(self.sender(), value)
        self.videoWidget.SetMagnifier(value)
        self.videoWidget.UpdateSurface()
        return

    def pointDrawer(self, value):
        ''' Draw Point '''
        self.UncheckUtils(self.sender(), value)
        self.videoWidget.SetPointDrawer(value)
        self.videoWidget.UpdateSurface()

    def lineDrawer(self, value):
        ''' Draw Line '''
        self.UncheckUtils(self.sender(), value)
        self.videoWidget.SetLineDrawer(value)
        self.videoWidget.UpdateSurface()

    def polygonDrawer(self, value):
        ''' Draw Polygon '''
        self.UncheckUtils(self.sender(), value)
        self.videoWidget.SetPolygonDrawer(value)
        self.videoWidget.UpdateSurface()

    def ojectTracking(self, value):
        ''' Object Tracking '''
        self.UncheckUtils(self.sender(), value)
        self.videoWidget.SetObjectTracking(value)
        self.videoWidget.UpdateSurface()

    def VideoRuler(self, value):
        ''' Video Ruler '''
        self.UncheckUtils(self.sender(), value)
        self.videoWidget.SetRuler(value)
        if value:
            self.player.pause()
            self.btn_play.setIcon(QIcon(":/imgFMV/images/play-arrow.png"))
        else:
            self.videoWidget.ResetDrawRuler()
            self.player.play()
            self.btn_play.setIcon(QIcon(":/imgFMV/images/pause.png"))

        self.videoWidget.UpdateSurface()

    def VideoCensure(self, value):
        ''' Censure Video Parts'''
        self.UncheckUtils(self.sender(), value)
        self.videoWidget.SetCensure(value)
        self.videoWidget.UpdateSurface()
        return

    def UncheckUtils(self, sender, value):
        ''' Uncheck Utils Video '''
        self.actionMagnifying_glass.setChecked(False)
        self.actionDraw_Pinpoint.setChecked(False)
        self.actionDraw_Line.setChecked(False)
        self.actionDraw_Polygon.setChecked(False)
        self.actionObject_Tracking.setChecked(False)
        self.actionRuler.setChecked(False)
        self.actionCensure.setChecked(False)

        self.videoWidget.RestoreDrawer()

        sender.setChecked(value)
        return

    def UncheckFilters(self, sender, value):
        ''' Uncheck Filters Video '''
        self.actionGray.setChecked(False)
        self.actionInvert_Color.setChecked(False)
        self.actionMono_Filter.setChecked(False)
        self.actionCanny_edge_detection.setChecked(False)
        self.actionAuto_Contrast_Filter.setChecked(False)
        self.actionMirroredH.setChecked(False)

        self.videoWidget.RestoreFilters()

        sender.setChecked(value)
        return

    # End Snnipet FILTERS

    def isMuted(self):
        ''' Is muted video property'''
        return self.playerMuted

    def setMuted(self):
        ''' Muted video '''
        if self.player.isMuted():
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_up.png"))
            self.player.setMuted(False)
            self.volumeSlider.setEnabled(True)
        else:
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_off.png"))
            self.player.setMuted(True)
            self.volumeSlider.setEnabled(False)
        return

    def stop(self):
        ''' Stop video'''
        # Prevent Error in a Video Utils.Disable Magnifier
        if self.actionMagnifying_glass.isChecked():
            self.actionMagnifying_glass.trigger()
        # Stop Video
        self.fakeStop()
        return

    def volume(self):
        ''' Volume Slider '''
        return self.volumeSlider.value()

    def setVolume(self, volume):
        ''' Tooltip and set Volume value and icon '''
        self.player.setVolume(volume)
        self.showVolumeTip(volume)
        if 0 < volume <= 30:
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_30.png"))
        elif 30 < volume <= 60:
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_60.png"))
        elif 60 < volume <= 100:
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_up.png"))
        elif volume == 0:
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_off.png"))

    def EndMedia(self):
        ''' Button end video position '''
        if self.player.isVideoAvailable():
            self.player.setPosition(self.player.duration())
            self.videoWidget.update()
        return

    def StartMedia(self):
        ''' Button start video position '''
        if self.player.isVideoAvailable():
            self.player.setPosition(0)
            self.videoWidget.update()
        return

    def forwardMedia(self):
        ''' Button forward Video '''
        forwardTime = int(self.player.position()) + 10 * 1000
        if forwardTime > int(self.player.duration()):
            forwardTime = int(self.player.duration())
        self.player.setPosition(forwardTime)

    def rewindMedia(self):
        ''' Button rewind Video '''
        rewindTime = int(self.player.position()) - 10 * 1000
        if rewindTime < 0:
            rewindTime = 0
        self.player.setPosition(rewindTime)

    def AutoRepeat(self, checked):
        ''' Button AutoRepeat Video '''
        if checked:
            self.playlist.setPlaybackMode(QMediaPlaylist.Loop)
        else:
            self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)
        return

    def showVolumeTip(self, _):
        ''' Volume Slider Tooltip Trick '''
        self.style = self.volumeSlider.style()
        self.opt = QStyleOptionSlider()
        self.volumeSlider.initStyleOption(self.opt)
        rectHandle = self.style.subControlRect(self.style.CC_Slider, self.opt,
                                               self.style.SC_SliderHandle)
        self.tip_offset = QPoint(5, 15)
        pos_local = rectHandle.topLeft() + self.tip_offset
        pos_global = self.volumeSlider.mapToGlobal(pos_local)
        QToolTip.showText(pos_global,
                          str(self.volumeSlider.value()) + " %", self)

    def showMoveTip(self, currentInfo):
        ''' Player Silder Move Tooptip Trick '''
        self.style = self.sliderDuration.style()
        self.opt = QStyleOptionSlider()
        self.sliderDuration.initStyleOption(self.opt)
        rectHandle = self.style.subControlRect(self.style.CC_Slider, self.opt,
                                               self.style.SC_SliderHandle)
        self.tip_offset = QPoint(5, 15)
        pos_local = rectHandle.topLeft() + self.tip_offset
        pos_global = self.sliderDuration.mapToGlobal(pos_local)

        tStr = _seconds_to_time(currentInfo)

        QToolTip.showText(pos_global, tStr, self)

    def durationChanged(self, duration):
        ''' Duration video change signal '''
        duration /= 1000
        self.duration = duration
        self.sliderDuration.setMaximum(duration)

    def positionChanged(self, progress):
        ''' Current Video position change '''
        progress /= 1000

        if not self.sliderDuration.isSliderDown():
            self.sliderDuration.setValue(progress)

        self.updateDurationInfo(progress)

    def updateDurationInfo(self, currentInfo):
        ''' Update labels duration Info and CallBack Metadata '''
        duration = self.duration
        self.currentInfo = currentInfo
        if currentInfo or duration:

            totalTime = _seconds_to_time(duration)
            currentTime = _seconds_to_time(currentInfo)
            tStr = currentTime + " / " + totalTime
            currentTimeInfo = _seconds_to_time_frac(currentInfo)
            # Get Metadata from buffer
            if not self.isStreaming:
                self.get_metadata_from_buffer(currentTimeInfo)
            else:
                qgsu.showUserAndLogMessage("", "Streaming on ", onlyLog=True)
                nextTime = currentInfo + self.pass_time / 1000
                nextTimeInfo = _seconds_to_time_frac(nextTime)
                self.callBackMetadata(currentTimeInfo, nextTimeInfo)

        else:
            tStr = ""

        self.labelDuration.setText(tStr)

    def handleCursor(self, status):
        ''' Change cursor '''
        if status in (QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia,
                      QMediaPlayer.StalledMedia):
            self.setCursor(Qt.BusyCursor)
        else:
            self.unsetCursor()

    def statusChanged(self, status):
        ''' Signal Status video change '''
        self.handleCursor(status)
        if status is QMediaPlayer.LoadingMedia or status is QMediaPlayer.StalledMedia or status is QMediaPlayer.InvalidMedia:
            self.videoAvailableChanged(False)
        elif status == QMediaPlayer.InvalidMedia:
            qgsu.showUserAndLogMessage(QCoreApplication.translate(
                "QgsFmvPlayer", self.player.errorString()),
                                       level=QGis.Warning)
            self.videoAvailableChanged(False)
        else:
            self.videoAvailableChanged(True)

    def playFile(self, videoPath):
        ''' Play file from path '''
        try:
            RemoveVideoLayers()
            RemoveGroupByName()
            #             if "udp://" in videoPath:
            #                 host, port = videoPath.split("://")[1].split(":")
            #                 receiver = UDPClient(host, int(port), type="udp")
            #                 receiver.show()
            #                 self.close()
            #                 return
            #             if "tcp://" in videoPath:
            #                 host, port = videoPath.split("://")[1].split(":")
            #                 receiver = UDPClient(host, port, type="tcp")
            #                 receiver.show()
            #                 self.close()
            #                 return
            self.fileName = videoPath
            self.playlist = QMediaPlaylist()
            if self.isStreaming:
                url = QUrl(videoPath)
            else:
                url = QUrl.fromLocalFile(videoPath)
            qgsu.showUserAndLogMessage("", "Added: " + str(url), onlyLog=True)
            self.playlist.addMedia(QMediaContent(url))
            self.player.setPlaylist(self.playlist)

            self.setWindowTitle(
                QCoreApplication.translate("QgsFmvPlayer", 'Playing : ') +
                os.path.basename(os.path.normpath(videoPath)))

            CreateVideoLayers()
            self.clearMetadata()

            self.HasFileAudio = True
            if not self.HasAudio(videoPath):
                self.actionAudio.setEnabled(False)
                self.actionSave_Audio.setEnabled(False)
                self.HasFileAudio = False

            # Recenter map on video initial point
            if self.initialPt:
                rect = QgsRectangle(self.initialPt[1], self.initialPt[0],
                                    self.initialPt[1], self.initialPt[0])
                self.iface.mapCanvas().setExtent(rect)
                self.iface.mapCanvas().refresh()

            self.playClicked(True)

        except Exception as e:
            qgsu.showUserAndLogMessage(QCoreApplication.translate(
                "QgsFmvPlayer", 'Open Video File : '),
                                       str(e),
                                       level=QGis.Warning)

    def ReciconUpdate(self, _):
        ''' Record Button Icon Effect '''
        self.btn_Rec.setIcon(QIcon(self.RecGIF.currentPixmap()))

    def StopRecordAnimation(self):
        '''Stop record gif animation'''
        self.RecGIF.frameChanged.disconnect(self.ReciconUpdate)
        self.RecGIF.stop()
        self.btn_Rec.setIcon(QIcon(":/imgFMV/images/record.png"))

    # TODO: Make in other thread
    def RecordVideo(self, value):
        ''' Cut Video '''
        currentTime = _seconds_to_time(self.currentInfo)

        if value is False:
            self.endRecord = currentTime
            _, file_extension = os.path.splitext(self.fileName)

            out, _ = askForFiles(self,
                                 QCoreApplication.translate(
                                     "QgsFmvPlayer", "Save video record"),
                                 isSave=True,
                                 exts=file_extension[1:])

            if not out:
                self.StopRecordAnimation()
                return

            p = _spawn([
                '-i', self.fileName, '-ss', self.startRecord, '-to',
                self.endRecord, '-preset', 'ultrafast', '-c', 'copy', out
            ])
            p.communicate()
            qgsu.showUserAndLogMessage(
                QCoreApplication.translate("QgsFmvPlayer",
                                           "Save file succesfully!"))

            self.StopRecordAnimation()
        else:
            self.startRecord = currentTime
            self.RecGIF.frameChanged.connect(self.ReciconUpdate)
            self.RecGIF.start()
        return

    def videoAvailableChanged(self, available):
        ''' Buttons for video available '''
        # self.btn_Color.setEnabled(available)
        self.btn_CaptureFrame.setEnabled(available)
        self.gb_PlayerControls.setEnabled(available)
        return

    def toggleGroup(self, state):
        ''' Toggle GroupBox '''
        sender = self.sender()
        if state:
            sender.setFixedHeight(sender.sizeHint().height())
        else:
            sender.setFixedHeight(15)

    def fakeStop(self):
        '''self.player.stop() make a black screen and not reproduce it again'''
        self.player.pause()
        self.StartMedia()
        self.btn_play.setIcon(QIcon(":/imgFMV/images/play-arrow.png"))

    def playClicked(self, _):
        ''' Stop and Play video '''
        if self.playerState in (QMediaPlayer.StoppedState,
                                QMediaPlayer.PausedState):
            self.btn_play.setIcon(QIcon(":/imgFMV/images/pause.png"))
            # Uncheck Ruler
            self.videoWidget.ResetDrawRuler()
            self.actionRuler.setChecked(False)
            self.videoWidget.SetRuler(False)
            # Play Video
            self.player.play()
        elif self.playerState == QMediaPlayer.PlayingState:
            self.btn_play.setIcon(QIcon(":/imgFMV/images/play-arrow.png"))
            self.player.pause()

    def seek(self, seconds):
        '''Slider Move'''
        self.player.setPosition(seconds * 1000)
        self.showMoveTip(seconds)

    def convertVideo(self):
        '''Convert Video To Other Format '''
        out, _ = askForFiles(self,
                             QCoreApplication.translate(
                                 "QgsFmvPlayer", "Save Video as..."),
                             isSave=True,
                             exts=[
                                 "mp4", "ogg", "avi", "mkv", "webm", "flv",
                                 "mov", "mpg", "mp3"
                             ])

        if not out:
            return

        # TODO : Make Correct format Conversion and embebed metadata
        info = self.converter.probeInfo(self.fileName)
        if info is not None:
            if self.HasFileAudio:
                audio_codec = info.audio.codec
                audio_samplerate = info.audio.audio_samplerate
                audio_channels = info.audio.audio_channels

            video_codec = info.video.codec
            video_width = info.video.video_width
            video_height = info.video.video_height
            video_fps = info.video.video_fps

        _, out_ext = os.path.splitext(out)

        if self.HasFileAudio:
            options = {
                'format': out_ext[1:],
                'audio': {
                    'codec': audio_codec,
                    'samplerate': audio_samplerate,
                    'channels': audio_channels
                },
                'video': {
                    'codec': video_codec,
                    'width': video_width,
                    'height': video_height,
                    'fps': video_fps
                }
            }
        else:
            options = {
                'format': out_ext[1:],
                'video': {
                    'codec': video_codec,
                    'width': video_width,
                    'height': video_height,
                    'fps': video_fps
                }
            }

        taskConvertVideo = QgsTask.fromFunction('Converting Video Task',
                                                self.converter.convert,
                                                infile=self.fileName,
                                                outfile=out,
                                                options=options,
                                                twopass=False,
                                                on_finished=self.finishedTask,
                                                flags=QgsTask.CanCancel)

        QgsApplication.taskManager().addTask(taskConvertVideo)

    def CreateBitratePlot(self):
        ''' Create video Plot Bitrate Thread '''
        sender = self.sender().objectName()

        if sender == "actionAudio":
            taskactionAudio = QgsTask.fromFunction(
                'Show Audio Bitrate',
                self.BitratePlot.CreatePlot,
                fileName=self.fileName,
                output=None,
                t='audio',
                on_finished=self.finishedTask,
                flags=QgsTask.CanCancel)

            QgsApplication.taskManager().addTask(taskactionAudio)

        elif sender == "actionVideo":
            taskactionVideo = QgsTask.fromFunction(
                'Show Video Bitrate',
                self.BitratePlot.CreatePlot,
                fileName=self.fileName,
                output=None,
                t='video',
                on_finished=self.finishedTask,
                flags=QgsTask.CanCancel)

            QgsApplication.taskManager().addTask(taskactionVideo)

        elif sender == "actionSave_Audio":
            fileaudio, _ = askForFiles(self,
                                       QCoreApplication.translate(
                                           "QgsFmvPlayer",
                                           "Save Audio Bitrate Plot"),
                                       isSave=True,
                                       exts=[
                                           "png", "pdf", "pgf", "eps", "ps",
                                           "raw", "rgba", "svg", "svgz"
                                       ])

            if not fileaudio:
                return

            taskactionSave_Audio = QgsTask.fromFunction(
                'Save Action Audio Bitrate',
                self.BitratePlot.CreatePlot,
                fileName=self.fileName,
                output=fileaudio,
                t='audio',
                on_finished=self.finishedTask,
                flags=QgsTask.CanCancel)

            QgsApplication.taskManager().addTask(taskactionSave_Audio)

        elif sender == "actionSave_Video":
            filevideo, _ = askForFiles(self,
                                       QCoreApplication.translate(
                                           "QgsFmvPlayer",
                                           "Save Video Bitrate Plot"),
                                       isSave=True,
                                       exts=[
                                           "png", "pdf", "pgf", "eps", "ps",
                                           "raw", "rgba", "svg", "svgz"
                                       ])

            if not filevideo:
                return

            taskactionSave_Video = QgsTask.fromFunction(
                'Save Action Video Bitrate',
                self.BitratePlot.CreatePlot,
                fileName=self.fileName,
                output=filevideo,
                t='video',
                on_finished=self.finishedTask,
                flags=QgsTask.CanCancel)

            QgsApplication.taskManager().addTask(taskactionSave_Video)

    def finishedTask(self, e, result=None):
        """ Common finish task function """
        if e is None:
            if result is None:
                qgsu.showUserAndLogMessage(QCoreApplication.translate(
                    "QgsFmvPlayer", 'Completed with no exception and no result '\
                    '(probably manually canceled by the user)'), level=QGis.Warning)
            else:
                if "Georeferencing" in result['task']:
                    return
                qgsu.showUserAndLogMessage(
                    QCoreApplication.translate(
                        "QgsFmvPlayer", "Succesfully " + result['task'] + "!"))
                if "Bitrate" in result['task']:
                    self.matplot = ShowPlot(self.BitratePlot.bitrate_data,
                                            self.BitratePlot.frame_count,
                                            self.fileName,
                                            self.BitratePlot.output)
                if result['task'] == 'Show Video Info Task':
                    self.showVideoInfoDialog(self.converter.bytes_value)
        else:
            qgsu.showUserAndLogMessage(QCoreApplication.translate(
                "QgsFmvPlayer", "Failed " + result['task'] + "!"),
                                       level=QGis.Warning)
            raise e

    def ExtractAllFrames(self):
        """ Extract All Video Frames Task """
        directory = askForFolder(
            self,
            QCoreApplication.translate("QgsFmvPlayer", "Save all Frames"),
            options=QFileDialog.DontResolveSymlinks | QFileDialog.ShowDirsOnly)

        if directory:
            taskExtractAllFrames = QgsTask.fromFunction(
                'Save All Frames Task',
                self.SaveAllFrames,
                fileName=self.fileName,
                directory=directory,
                on_finished=self.finishedTask,
                flags=QgsTask.CanCancel)

            QgsApplication.taskManager().addTask(taskExtractAllFrames)
        return

    def SaveAllFrames(self, task, fileName, directory):
        vidcap = cv2.VideoCapture(fileName)
        length = int(vidcap.get(cv2.CAP_PROP_FRAME_COUNT))
        count = 0
        while not task.isCanceled():
            _, image = vidcap.read()
            cv2.imwrite(directory + "\\frame_%d.jpg" % count,
                        image)  # save frame as JPEG file
            task.setProgress(count * 100 / length)
            count += 1
        vidcap.release()
        cv2.destroyAllWindows()
        if task.isCanceled():
            return None
        return {'task': task.description()}

    def ExtractCurrentFrame(self):
        """ Extract Current Frame Task """
        image = self.videoWidget.GetCurrentFrame()
        output, _ = askForFiles(self,
                                QCoreApplication.translate(
                                    "QgsFmvPlayer", "Save Current Frame"),
                                isSave=True,
                                exts=["png", "jpg", "bmp", "tiff"])

        if not output:
            return

        taskCurrentFrame = QgsTask.fromFunction('Save Current Frame Task',
                                                self.SaveCapture,
                                                image=image,
                                                output=output,
                                                on_finished=self.finishedTask,
                                                flags=QgsTask.CanCancel)

        QgsApplication.taskManager().addTask(taskCurrentFrame)
        return

    def SaveCapture(self, task, image, output):
        ''' Save Current Frame '''
        image.save(output)
        if task.isCanceled():
            return None
        return {'task': task.description()}

    def OpenQgsFmvMetadata(self):
        """ Open Metadata Dock """
        if self.metadataDlg is None:
            self.metadataDlg = QgsFmvMetadata(parent=self, player=self)
            self.addDockWidget(Qt.RightDockWidgetArea, self.metadataDlg)
            self.metadataDlg.show()
        else:
            self.metadataDlg.show()
        return

    def showVideoInfoDialog(self, outjson):
        """ Show Video Information Dialog """
        view = QTreeView()
        model = QJsonModel()
        view.setModel(model)
        model.loadJsonFromConsole(outjson)

        self.VideoInfoDialog = QDialog(self)
        self.VideoInfoDialog.setWindowTitle(
            QCoreApplication.translate("QgsFmvPlayer", "Video Information : ")
            + self.fileName)
        self.VideoInfoDialog.setWindowIcon(
            QIcon(":/imgFMV/images/video-info.png"))

        self.verticalLayout = QVBoxLayout(self.VideoInfoDialog)
        self.verticalLayout.addWidget(view)
        view.expandAll()
        view.header().setSectionResizeMode(QHeaderView.ResizeToContents)

        self.VideoInfoDialog.setWindowFlags(Qt.Window
                                            | Qt.WindowCloseButtonHint)
        self.VideoInfoDialog.setObjectName("VideoInfoDialog")
        self.VideoInfoDialog.resize(500, 400)
        self.VideoInfoDialog.show()

    def closeEvent(self, _):
        """ Close Event """
        self.stop()
        self.parent._PlayerDlg = None
        self.parent.ToggleActiveFromTitle()
        RemoveVideoLayers()
        RemoveGroupByName()
        ResetData()

        try:
            self.metadataDlg.hide()
        except Exception:
            None
        try:
            self.matplot.close()
        except Exception:
            None
        # Restore Filters State
        self.videoWidget.RestoreFilters()
Exemple #5
0
class VideoPlayer(QWidget):
    
    def __init__(self, parent=None):
        global gantChart
        global Topics
        
        super(VideoPlayer, self).__init__(parent)
        self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.mediaPlayer.setVolume(0)
        
        #Parse json file
        videoGlobals.classLabels, videoGlobals.highLabels, videoGlobals.annotationColors, videoGlobals.eventColors = self.parseJson()
        
        Topics              = None
        self.time_          = 0
        self.time_dif       = 0
        self.duration       = 0
        self.message_count  = 0
        self.videobox       = []
        self.box_buffer     = []
        self.metric_buffer  = []
        
        #Audio variables
        self.player = QMediaPlayer()
        self.playlist = QMediaPlaylist(self)
        self.playFlag = False
        
        self.topic_window = topicBox.TopicBox()
        
        # >> DEFINE WIDGETS OCJECTS
        # >> VIDEO - AUDIO - GANTT CHART
        #----------------------
        self.videoWidget = VideoWidget()
        self.videoWidget.setFixedSize(640, 480)

        #Video buttons
        videoLayout = self.createVideoButtons()
                
        #Video Gantt Chart
        self.gantt = gantChart.gantShow()
        gantChart = self.gantt
        gantChart.axes.get_xaxis().set_visible(False)
        gantChart.setFixedSize(1300, 90)
        
        #Create Slider
        self.createSlider()
        
        self.controlEnabled = False

        #Specify video layout align
        laserAndVideoLayout = QHBoxLayout()
        laserAndVideoLayout.addLayout(videoLayout)

        #Audio Player buttons
        buttonLayoutAudio = self.createAudioButtons()
        waveLayout = self.createAudio()
        
        
        self.mainLayout = QVBoxLayout()
        self.mainLayout.addLayout(laserAndVideoLayout)
        self.mainLayout.addWidget(self.positionSlider)
        self.mainLayout.addWidget(self.gantt)
        self.mainLayout.addLayout(waveLayout)
        self.mainLayout.addLayout(buttonLayoutAudio)

        self.setLayout(self.mainLayout)

        self.mediaPlayer.setVideoOutput(self.videoWidget.videoSurface())
        self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
        self.mediaPlayer.positionChanged.connect(self.positionChanged)
        self.mediaPlayer.durationChanged.connect(self.durationChanged)

    def createSlider(self):
        self.positionSlider = QSlider(Qt.Horizontal)
        self.positionSlider.setMinimum(0)
        self.positionSlider.setMaximum(self.duration)
        self.positionSlider.setTickInterval(1)
        self.positionSlider.sliderMoved.connect(self.setPosition)

    def createVideoButtons(self):
        
        verticalLine 	=  QFrame()
        verticalLine.setFrameStyle(QFrame.VLine)
        verticalLine.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Expanding)
        
        self.playButton = QPushButton()
        self.playButton.setEnabled(False)
        self.playButton.setShortcut(QKeySequence(Qt.Key_Space))
        self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
        self.playButton.clicked.connect(self.play)

              
        self.previousButton = QPushButton()
        self.previousButton.setIcon(self.style().standardIcon(QStyle.SP_MediaSeekBackward))
        self.previousButton.setShortcut(QKeySequence(Qt.ALT + Qt.Key_A))
        self.previousButton.clicked.connect(self.previousFrame)
        
        self.nextButton = QPushButton()
        self.nextButton.setIcon(self.style().standardIcon(QStyle.SP_MediaSeekForward))
        self.nextButton.setShortcut(QKeySequence(Qt.ALT + Qt.Key_D))
        self.nextButton.clicked.connect(self.nextFrame)        
        
        
         #add label to slider about elapsed time
        self.label_tmp = '<b><FONT SIZE=3>{}</b>'
        self.timelabel = QLabel(self.label_tmp.format('Time: ' + str(self.duration)))


        self.label = QHBoxLayout()
        self.label.addWidget(self.timelabel)
        self.label.setAlignment(Qt.AlignRight)
        
        
        self.controlLayout = QHBoxLayout()
        self.controlLayout.addWidget(self.playButton)
        self.controlLayout.addWidget(self.previousButton)
        self.controlLayout.addWidget(self.nextButton)
        self.controlLayout.setAlignment(Qt.AlignLeft)
        
        self.newLayout = QHBoxLayout()
        self.newLayout.addLayout(self.controlLayout)
        self.newLayout.addLayout(self.label)
        
        videoLayout = QVBoxLayout()
        videoLayout.addWidget(self.videoWidget)
        videoLayout.addLayout(self.newLayout)
        
        return videoLayout
        
    def pauseMedia(self):
        self.mediaPlayer.pause()
        self.Pause()

    def previousFrame(self):
        global frameCounter
        if frameCounter > 0:
            frameCounter -= 2
            pos = round(((frameCounter ) * (self.duration * 1000)) / self.message_count)
            self.mediaPlayer.setPosition(pos) 
        
    def nextFrame(self):
        global frameCounter
    
        if frameCounter < self.message_count:
            pos = round(((frameCounter ) * (self.duration * 1000)) / self.message_count)
            self.mediaPlayer.setPosition(pos) 
        
    # AUDIO PLAYER BUTTON FUNCTIONS
    def createAudio(self):
        #Define Audio annotations and gantt chart
        self.wave = vA.Waveform()
        audioGlobals.fig = self.wave
        self.wave.axes.get_xaxis().set_visible(False)
        self.wave.draw()
        self.wave.setFixedSize(1300, 175)
        
        self.audioChart = gA.Chart()
        audioGlobals.chartFig = self.audioChart
        self.audioChart.setFixedSize(1300, 90)
        
        #Audio layouts
        waveLayout = QVBoxLayout()
        waveLayout.addWidget(self.wave)
        waveLayout.addWidget(self.audioChart)
        
        return waveLayout
        
    def createAudioButtons(self):
        self.playButtonAudio = QPushButton()
        self.stopButtonAudio = QPushButton()

        self.playButtonAudio.clicked.connect(self.audioPlay)
        
        self.playButtonAudio.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
        
        buttonLayoutAudio = QHBoxLayout()
        buttonLayoutAudio.addWidget(self.playButtonAudio)
        buttonLayoutAudio.setAlignment(Qt.AlignLeft)
        
        return buttonLayoutAudio
       
    #Play audio (whole signal or segment)
    def audioPlay(self):

        #GET CLICKS FROM WAVEFORM
        #Initialize connection-position ONCE
        if not audioGlobals.playerStarted:
            #10ms for changePosition -> Not Delaying
            self.player.positionChanged.connect(self.checkPositionToStop)
            self.player.setNotifyInterval(10)
            audioGlobals.playerStarted = True
            if audioGlobals.durationFlag in [0, 1]:
                self.end = audioGlobals.duration*1000 - 10
                audioGlobals.endTimeToPlay = self.end
            else:
                self.end = audioGlobals.endTimeToPlay
                
                
            self.start = audioGlobals.startTimeToPlay
            self.player.setPosition(self.start)
            
       
        if self.playFlag:
            self.playFlag = False
            audioGlobals.playerStarted = True
            self.player.setPosition(self.time_)
            self.player.pause()
            self.playButtonAudio.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
        else:
            self.playFlag = True
            self.playButtonAudio.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
            self.player.play()

    #Stop audio playing
    def audioStop(self):
        self.player.stop()
        #Begin again segment
        self.start = audioGlobals.startTimeToPlay
        self.player.setPosition(self.start)

    #Check ms in audio to stop play
    def checkPositionToStop(self):
        self.time_ = self.player.position()
        if not video_player:
            audioGlobals.fig.drawNew(float(self.time_)/1000)
            audioGlobals.fig.draw()
        if self.time_ >= self.end:
            self.audioStop()
            self.player.setPosition(self.start)

    def videoPosition(self):
        self.videoTime = self.mediaPlayer.position()

    def openFile(self):
        global framerate
        global bagFile
        global rgbFileName
        global Topics
        global audio_player
        global video_player
        framerate = 0
               
        fileName, _ = QFileDialog.getOpenFileName(self, "Open Bag", QDir.currentPath(),"(*.bag *.avi *.mp4 *.mkv *.mp3 *.wav)")
        # create a messsage box for get or load data info
        if fileName:
            name, extension = os.path.splitext(fileName)
            self.videobox = []
            if extension == '.bag':
                bagFile = fileName
                try:
                    bag = rosbag.Bag(fileName)
                    Topics, self.duration = get_bag_metadata(bag)
                    #Show window to select topics
                    self.topic_window.show_topics(Topics)
                except:
                    self.errorMessages(0)
                        
                #Audio Handling
                if self.topic_window.temp_topics[0][1] != 'Choose Topic':
                    try:
                        audio_player = True
                        audioGlobals.annotations = []
                        rosbagAudio.runMain(bag, str(fileName))
                    except:
                        self.errorMessages(6)
                    
                    #DEFINE PLAYER-PLAYLIST
                    #----------------------
                    self.source = QUrl.fromLocalFile(os.path.abspath(audioGlobals.wavFileName))
                    self.content = QMediaContent(self.source)
                    self.playlist.addMedia(self.content)
                    self.player.setPlaylist(self.playlist)

                    self.wave.drawWave()
                    self.wave.drawAnnotations()
                    self.wave.draw()
                    self.audioChart.drawChart()
                    self.audioChart.draw()                        
                #RGB Handling
                if self.topic_window.temp_topics[2][1] != 'Choose Topic':
                    try:
                        video_player = True
                        rgbFileName = fileName.replace(".bag","_RGB.avi")

                        if not os.path.isfile(rgbFileName):
                            self.message_count, compressed, framerate = rosbagVideo.buffer_bag_metadata(bag, self.topic_window.temp_topics[2][1])
                            #Get bag video metadata
                            print('Getting rgb data from ROS', 'green')
                            image_buffer = rosbagRGB.buffer_rgb_data(bag, self.topic_window.temp_topics[2][1], compressed)
                            if not image_buffer:
                                raise Exception(8)
                            result  = rosbagRGB.write_rgb_video(rgbFileName, image_buffer, framerate)
                            if not result:
                                raise Exception(2)
                        
                        self.duration, framerate, self.message_count =  rosbagRGB.get_metadata(rgbFileName)
                        
                        # just fill time buffer in case that video exists
                        start_time = None
                        for topic, msg, t in bag.read_messages(topics=[self.topic_window.temp_topics[2][1]]):
                            if not start_time:
                                start_time = t.to_sec()
                            time = t.to_sec() - start_time
                            self.videobox.append(boundBox(time))
                            
                        self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(os.path.abspath(rgbFileName))))
                        self.playButton.setEnabled(True)
                     
                                        
                                    
                    except Exception as e:
                        print(e)
                        self.errorMessages(e[0])	                            
            else:
                if extension in video_extensions:
                    video_player = True
                    self.duration, framerate, self.message_count  =  rosbagRGB.get_metadata(fileName)
                    self.videobox = [boundBox(count/framerate) for count in xrange(int(self.message_count))] 
                    self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(os.path.abspath(fileName))))
                    self.playButton.setEnabled(True)
                    rgbFileName = fileName
                
                try:
                    audioGlobals.annotations = []
                    rosbagAudio.runMain(None, str(fileName))
                    
                    #DEFINE PLAYER-PLAYLIST
                    #----------------------
                    print audioGlobals.wavFileName
                    self.source = QUrl.fromLocalFile(os.path.abspath(audioGlobals.wavFileName))
                    self.content = QMediaContent(self.source)
                    self.playlist.addMedia(self.content)
                    self.player.setPlaylist(self.playlist)
        
                    self.wave.drawWave()
                    self.wave.drawAnnotations()
                    self.wave.draw()
                    self.audioChart.drawChart()
                    self.audioChart.draw()
                    audio_player = True
                    
                except:
                    print "No audio found"
                
            
            mainWindow.repaint(player.videobox, framerate)
            mainWindow.setWindowTitle(fileName)    
            self.setWindowTitle(fileName + ' -> Annotation')
     
    #Open CSV file
    def openCsv(self):
        global framerate
        global rgbFileName
        global bagFile
        global videoCSV
        global headlines
        self.box_buffer = []
        
        if rgbFileName is not None:
            
            # OPEN VIDEO - AUDIO
            fileName,_ =  QFileDialog.getOpenFileName(self, "Open Csv ", os.path.dirname(os.path.abspath(rgbFileName)),"(*.csv)")
            if fileName:
                videoCSV = fileName
                self.videobox = [boundBox(count) for count in xrange(int(self.message_count))]
                headlines, box_buff, box_action, features = rosbagRGB.buffer_video_csv(fileName)
                if not (box_buff):
                    self.errorMessages(1)
                else:
                    self.box_buffer = [list(elem) for elem in box_buff]
                    
                    #Frame counter initialize
                    timestamp = None
                    counter = 0
                    self.box_actionBuffer = [key for key in box_action]
                    self.features = [key for key in features]
                    for i, key in enumerate(self.box_buffer):
                        if timestamp is not None:
                            if timestamp != key[0]:
                                counter += 1
                        self.videobox[counter].addBox(key[0], key[1], key[2:], self.box_actionBuffer[i], features[i])
                        timestamp  = key[0]
                              
                    mainWindow.repaint(player.videobox, framerate)
        else:
            self.errorMessages(10)
	
    #Writes the boxes to csv
    def writeCSV(self):
        global headlines
        global rgbFileName
        global video_player
        if video_player:
            name, extension = os.path.splitext(rgbFileName)
            csvFileName = rgbFileName.replace(extension,"_video.csv")
            with open(csvFileName, 'w') as file:
                csv_writer = csv.writer(file, delimiter='\t')
                csv_writer.writerow(headlines)
                for i in xrange(0, len(self.videobox)):
                    box = self.videobox[i]
                    if len(box.box_id) > 0:
                        for j in xrange(0, len(box.box_id)):
                            master = []
                            append = master.append
                            if box.box_id[j] != -1:
                                append(box.timestamp)
                                append(box.box_id[j])
                                for param in box.box_Param[j][::]:
                                    append(param)
                                for param in box.features[j][::]:
                                    append(param)
                                append(box.annotation[j])    
                                
                                csv_writer.writerow(master)
                            else:
                                csv_writer.writerow([box.timestamp])
                    else:
						csv_writer.writerow([box.timestamp])
                    
                print ("Video csv written at: ", csvFileName) 
                
    def errorMessages(self, index):
        msgBox = QMessageBox()
        msgBox.setIcon(msgBox.Warning)
        if index == 0:
            msgBox.setWindowTitle("Open rosbag")
            msgBox.setText("Could not open rosbag")
        elif index == 1:
            msgBox.setWindowTitle("Open CSV")
            msgBox.setText("Could not process CSV file")
        elif index == 2:
            msgBox.setWindowTitle("Open rosbag")
            msgBox.setIcon(msgBox.Critical)
            msgBox.setText("Could not write video")
        elif index == 3:
            msgBox.setText("Error: Json file path error")
        elif index == 4:
            msgBox.setText("Not integer type")
        elif index == 5:
            msgBox.setText("Box id already given")
        elif index == 6:
            msgBox.setWindowTitle("Open rosbag")
            msgBox.setText("Incorrect Audio Topic")
        elif index == 8:
            msgBox.setWindowTitle("Open rosbag")
            msgBox.setText("Incorrect RGB Topic")
        elif index == 10:
            msgBox.setWindowTitle("Open CSV")
            msgBox.setText("You must select a rosbag first")

        msgBox.resize(100,40)
        msgBox.exec_()

    def play(self):
        global frameCounter
        global audio_player
        global video_player
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.videoPosition()
            self.mediaPlayer.pause()
            if audio_player:
                self.audioPlay()
            self.time_ = self.positionSlider
        else:
            self.time_ = self.mediaPlayer.position()
            if audio_player:
                self.player.setPosition(self.time_)
                self.end = audioGlobals.duration*1000 - 10
                self.audioPlay()
            if video_player:
                self.mediaPlayer.play()

        # >> Get slider position for bound box
        posSlider = self.positionSlider.value()
        #self.tickLabel.setAlignment(posSlider)
        frameCounter = int(round((self.message_count * posSlider)/(self.duration * 1000)))


    def mediaStateChanged(self, state):
        if state == QMediaPlayer.PlayingState:
            self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
        else:
            self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))

    def positionChanged(self, position):
        time = "{0:.2f}".format(float(position)/1000)
        self.positionSlider.setValue(position)
        self.positionSlider.setToolTip(str(time) + ' sec')
        self.timelabel.setText(self.label_tmp.format('Time: ' + str(time) + '/ ' + str("{0:.2f}".format(self.duration)) + ' sec'))
        
        if audioGlobals.duration > 0 and self.mediaPlayer.state() != 0:
            audioGlobals.fig.drawNew(time)
            audioGlobals.fig.draw()

    def keyPressEvent(self,event):
        if event.key() == Qt.Key_Control:
            self.controlEnabled = True

    def keyReleaseEvent(self,event):
        if event.key() == Qt.Key_Control:
            self.controlEnabled = False

    def durationChanged(self, duration):
        self.positionSlider.setRange(0, duration)

    def setPosition(self, position):
        global frameCounter
        global audio_player
        global video_player
        
        frameCounter = int(round(self.message_count * position/(self.duration * 1000)))
        if frameCounter >= self.message_count:
            frameCounter = self.message_count - 1 
        if video_player:
            self.mediaPlayer.setPosition(position)
        if audio_player:
            self.player.setPosition(position)
            
            
    def closeEvent(self, event):
        self.writeCSV()
        eA.writeCSV()
    
    def parseJson(self):
        json_basicLabel = []
        json_highLabel = []
        json_annotationColors = []
        json_eventColors = []

        with open("labels.json") as json_file:
                json_data = json.load(json_file)
                for i in json_data['basiclabels'] :
                    json_basicLabel.append(i)
                for i in json_data['highlevellabels']:
                    json_highLabel.append(i)
                for i in json_data['annotationColors'] :
                    json_annotationColors.append(i)
                for i in json_data['eventColors']:
                    json_eventColors.append(i)
        return json_basicLabel,json_highLabel, json_annotationColors, json_eventColors
class MainWindow(qtw.QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.show()

        self.samples = None
        self.sampling_rate = None
        self.samples_after = None

        self.first_turn = True  # This Prevents the Spectrogram range variables from being overwritten

        self.PLOT_DIR = 'Plots'
        self.PDF_DIR = 'PDFs'
        self.ui.save_session_data.clicked.connect(lambda: self.save_session())
        self.ui.actionSave.triggered.connect(lambda: self.save_session())

        self.audio_player_before = QMediaPlayer()
        self.audio_player_after = QMediaPlayer()
        self.audio_player_before.setNotifyInterval(1)
        self.audio_player_after.setNotifyInterval(1)

        self.bands_powers = [0.0, 0.25, 0.50, 0.75, 1.0, 2.0, 3.0, 4.0, 5.0]

        self.spectrogram_power_range = {
            'min': np.array([]),
            'max': np.array([])
        }

        self.ui.min_pixel_intensity.sliderReleased.connect(
            lambda: self.spectrogram_pixels_intensity('min'))
        self.ui.max_pixel_intensity.sliderReleased.connect(
            lambda: self.spectrogram_pixels_intensity('max'))

        self.spectrogram_time_min, self.spectrogram_time_max = 0, 0  # Sync With Play

        self.band_slider = {}
        self.band_label = {}

        for index in range(10):
            self.band_slider[index] = getattr(self.ui,
                                              'band_{}'.format(index + 1))
            self.band_label[index] = getattr(self.ui,
                                             'band_{}_label'.format(index + 1))

        for slider in self.band_slider.values():
            slider.setDisabled(True)
            slider.setStyleSheet('selection-background-color: grey')

        for index, slider in self.band_slider.items():
            slider.sliderReleased.connect(
                lambda index=index: self.slider_gain_updated(index))

        self.available_palettes = [
            'twilight', 'Blues', 'Greys', 'ocean', 'nipy_spectral'
        ]
        self.current_color_palette = self.available_palettes[0]

        self.modified_signal = np.array([])
        self.current_slider_gain = [1.0] * 10

        self.controlers = {'before': [], 'after': []}

        for button, function in zip(
            ['zoom_in', 'zoom_out', 'jump_forward', 'jump_back'],
            [self.zoomin, self.zoomout, self.forward, self.back]):
            self.controlers['before'].append(
                (getattr(self.ui, '{}_btn_before'.format(button)), function))
            self.controlers['after'].append(
                (getattr(self.ui, '{}_btn_after'.format(button)), function))

        for channel in self.controlers.values():
            for signal in channel:
                signal[0].clicked.connect(signal[1])

        self.plot_widget = {
            'before': self.ui.graph_before,
            'after': self.ui.graph_after
        }

        self.spectrogram_widget = {
            'before': self.ui.spectrogram_before,
            'after': self.ui.spectrogram_after
        }

        self.data_line = {'before': None, 'after': None}

        self.playback_position = {'before': None, 'after': None}

        self.time_seeker = {
            'before': self.ui.time_seeker_before,
            'after': self.ui.time_seeker_after
        }

        self.total_time = {
            'before': self.ui.total_time_before,
            'after': self.ui.total_time_after
        }

        self.ui.actionExit.triggered.connect(self.close)
        self.ui.actionNew.triggered.connect(self.new_instance)
        self.ui.actionOpen.triggered.connect(self.open_audio_file)

        self.ui.play_btn_before.clicked.connect(self.audio_player_before.play)
        self.ui.pause_btn_before.clicked.connect(
            self.audio_player_before.pause)
        self.ui.stop_btn_before.clicked.connect(self.audio_player_before.stop)

        self.ui.play_btn_after.clicked.connect(self.audio_player_after.play)
        self.ui.pause_btn_after.clicked.connect(self.audio_player_after.pause)
        self.ui.stop_btn_after.clicked.connect(self.audio_player_after.stop)

        self.ui.palettes_box.currentTextChanged.connect(self.change_palette)

        self.ui.playback_speed_before.currentIndexChanged.connect(
            lambda: self.audio_player_before.setPlaybackRate(
                float(self.ui.playback_speed_before.currentText()[1:])))
        self.audio_player_before.durationChanged.connect(
            lambda duration: self.update_duration(duration, 'before'))

        self.ui.playback_speed_after.currentIndexChanged.connect(
            lambda: self.audio_player_after.setPlaybackRate(
                float(self.ui.playback_speed_after.currentText()[1:])))
        self.audio_player_after.durationChanged.connect(
            lambda duration: self.update_duration(duration, 'after'))

    def new_instance(self):
        self.new_instance = MainWindow()
        self.new_instance.show()

    def open_audio_file(self):
        path = qtw.QFileDialog.getOpenFileName(None, 'Load Audio', './',
                                               "Audio File(*.wav)")[0]

        for slider in self.band_slider.values():
            slider.setDisabled(False)
            slider.setStyleSheet('selection-background-color: blue')

        self.ui.max_pixel_intensity.setDisabled(False)
        self.ui.max_pixel_intensity.setStyleSheet(
            'selection-background-color: blue')
        self.ui.min_pixel_intensity.setDisabled(False)
        self.ui.min_pixel_intensity.setStyleSheet(
            'selection-background-color: blue')

        self.audio_player_before.setMedia(
            QMediaContent(qtc.QUrl.fromLocalFile(path)))
        self.audio_player_before.positionChanged.connect(
            lambda position: self.update_timestamp(
                position, self.ui.current_time_before, self.ui.
                time_seeker_before, 'before'))
        self.ui.time_seeker_before.valueChanged.connect(
            self.audio_player_before.setPosition)
        self.sampling_rate, self.samples = scipy.io.wavfile.read(path)
        self.plot_graph(self.samples, self.sampling_rate, 'before')
        self.plot_spectrogram(self.samples, self.sampling_rate, 'before')
        self.modify_signal()

    def plot_graph(self, samples, sampling_rate, widget):
        peak_value = np.amax(samples)
        normalized_data = samples / peak_value
        length = samples.shape[0] / sampling_rate
        time = list(np.linspace(0, length, samples.shape[0]))

        drawing_pen = pg.mkPen(color=(255, 0, 0), width=0.5)
        self.plot_widget[widget].removeItem(self.data_line[widget])
        self.data_line[widget] = self.plot_widget[widget].plot(time,
                                                               normalized_data,
                                                               pen=drawing_pen)
        self.plot_widget[widget].plotItem.setLabel(axis='left',
                                                   text='Normalized Amplitude')
        self.plot_widget[widget].plotItem.setLabel(axis='bottom',
                                                   text='time [s]')
        self.plot_widget[widget].plotItem.getViewBox().setLimits(
            xMin=0, xMax=np.max(time), yMin=-1.1, yMax=1.1)
        self.spectrogram_time_min, self.spectrogram_time_max = self.plot_widget[
            widget].plotItem.getAxis('bottom').range
        self.playback_position[widget] = pyqtgraph.LinearRegionItem(values=(0,
                                                                            0))
        self.plot_widget[widget].plotItem.getViewBox().addItem(
            self.playback_position[widget])

    def plot_spectrogram(self, samples, sampling_rate, widget):
        self.spectrogram_widget[widget].getFigure().clear()
        spectrogram_axes = self.spectrogram_widget[widget].getFigure(
        ).add_subplot(111)

        data = samples.astype('float32')
        frequency_magnitude = np.abs(librosa.stft(data))**2

        mel_spectrogram = librosa.feature.melspectrogram(S=frequency_magnitude,
                                                         y=data,
                                                         sr=sampling_rate,
                                                         n_mels=128)

        decibel_spectrogram = librosa.power_to_db(mel_spectrogram, ref=np.max)
        if self.first_turn:
            min_intensity = np.ceil(np.amin(decibel_spectrogram))
            max_intensity = np.ceil(np.amax(decibel_spectrogram))

            self.spectrogram_power_range['min'] = np.linspace(
                min_intensity, min_intensity / 2, 10).astype('int')
            self.spectrogram_power_range['min'] = np.append(
                self.spectrogram_power_range['min'],
                np.array([self.spectrogram_power_range['min'][0]]))
            self.spectrogram_power_range['max'] = np.linspace(
                (min_intensity + 1) / 2, max_intensity, 10).astype('int')
            self.spectrogram_power_range['max'] = np.append(
                self.spectrogram_power_range['max'],
                np.array([self.spectrogram_power_range['max'][-1]]))
            self.ui.min_pixel_intensity_lab.setText(
                str(self.spectrogram_power_range['min'][-1]))
            self.ui.max_pixel_intensity_lab.setText(
                str(self.spectrogram_power_range['max'][-1]))
            self.first_turn = False

        spectrogram_image = librosa.display.specshow(
            decibel_spectrogram,
            x_axis='time',
            y_axis='mel',
            sr=sampling_rate,
            ax=spectrogram_axes,
            cmap=self.current_color_palette,
            vmin=self.spectrogram_power_range['min'][-1],
            vmax=self.spectrogram_power_range['max'][-1])

        self.spectrogram_widget[widget].getFigure().colorbar(
            spectrogram_image, ax=spectrogram_axes, format='%+2.0f dB')
        spectrogram_axes.set(
            xlim=[self.spectrogram_time_min, self.spectrogram_time_max])
        self.spectrogram_widget[widget].draw()

    def modify_signal(self):
        frequency_content = np.fft.rfftfreq(len(self.samples),
                                            d=1 / self.sampling_rate)
        modified_signal = np.fft.rfft(self.samples)
        for index, slider_gain in enumerate(self.current_slider_gain):
            frequency_range_min = (index + 0) * self.sampling_rate / (2 * 10)
            frequency_range_max = (index + 1) * self.sampling_rate / (2 * 10)

            range_min_frequency = frequency_content > frequency_range_min
            range_max_frequency = frequency_content <= frequency_range_max

            slider_min_max = []
            for is_in_min_frequency, is_in_max_frequency in zip(
                    range_min_frequency, range_max_frequency):
                slider_min_max.append(is_in_min_frequency
                                      and is_in_max_frequency)

            modified_signal[slider_min_max] *= slider_gain

        self.samples_after = np.fft.irfft(modified_signal)

        self.save_output_wav()

        self.plot_graph(self.samples_after, self.sampling_rate, 'after')
        self.plot_spectrogram(self.samples_after, self.sampling_rate, 'after')

    def spectrogram_pixels_intensity(self, widget):
        slider = getattr(self.ui, '{}_pixel_intensity'.format(widget))
        self.spectrogram_power_range[widget][
            -1] = self.spectrogram_power_range[widget][int(slider.value())]
        label = getattr(self.ui, '{}_pixel_intensity_lab'.format(widget))
        label.setText(str(self.spectrogram_power_range[widget][-1]))
        self.plot_spectrogram(self.samples, self.sampling_rate, 'before')
        self.plot_spectrogram(self.samples_after, self.sampling_rate, 'after')

    def change_palette(self):
        self.current_color_palette = self.available_palettes[
            self.ui.palettes_box.currentIndex()]
        self.plot_spectrogram(self.samples, self.sampling_rate, 'before')
        self.plot_spectrogram(self.samples_after, self.sampling_rate, 'after')

    def slider_gain_updated(self, index):
        slider_gain = self.bands_powers[self.band_slider[index].value()]
        self.band_label[index].setText(f'{slider_gain}')
        self.current_slider_gain[index] = slider_gain
        self.modify_signal()

    def update_duration(self, duration, widget):
        self.time_seeker[widget].setMaximum(duration)

        if duration >= 0:
            self.total_time[widget].setText(time_stamp(duration))

    def update_timestamp(self, position, currentTimeLabel, timeSlider, widget):
        if position >= 0:
            currentTimeLabel.setText(time_stamp(position))

        timeSlider.blockSignals(True)
        timeSlider.setValue(position)
        timeSlider.blockSignals(False)

        self.playback_position[widget].setRegion(
            (position / 1000, position / 1000))
        minRange, maxRange = self.plot_widget[widget].plotItem.getAxis(
            'bottom').range
        if (position >= maxRange * 1000):
            self.plot_widget[widget].plotItem.getViewBox().translateBy(
                (maxRange - minRange), 0)
            self.synchronize()

        if (position <= minRange * 1000):
            self.plot_widget[widget].plotItem.getViewBox().translateBy(
                -minRange)
            self.synchronize()

    def zoomin(self) -> None:
        self.ui.graph_before.plotItem.getViewBox().scaleBy((0.75, 1.0))
        self.synchronize()

    def zoomout(self) -> None:
        self.ui.graph_before.plotItem.getViewBox().scaleBy((1.25, 1.0))
        self.synchronize()

    def back(self):
        self.ui.graph_before.plotItem.getViewBox().translateBy((-0.5, 0.0))
        self.synchronize()

    def forward(self):
        self.ui.graph_before.plotItem.getViewBox().translateBy((0.5, 0.0))
        self.synchronize()

    def synchronize(self):
        self.ui.graph_before.plotItem.getViewBox().setXLink(
            self.ui.graph_after.plotItem)
        self.spectrogram_time_min, self.spectrogram_time_max = self.ui.graph_before.plotItem.getAxis(
            'bottom').range
        self.plot_spectrogram(self.samples, self.sampling_rate, 'before')
        self.plot_spectrogram(self.samples_after, self.sampling_rate, 'after')

    def save_output_wav(self):
        try:
            shutil.rmtree('wav')
            os.mkdir('wav')
        except:
            os.mkdir('wav')
        self.now = datetime.now()
        self.now = f'{self.now:%Y-%m-%d %H-%M-%S.%f %p}'
        scipy.io.wavfile.write(f"wav/SBME{self.now}.wav", self.sampling_rate,
                               self.samples_after.astype(np.int16))
        path = os.listdir('wav')
        self.audio_player_after.setMedia(
            QMediaContent(qtc.QUrl.fromLocalFile(f'wav/{path[0]}')))
        self.audio_player_after.positionChanged.connect(
            lambda position: self.update_timestamp(position, self.ui.
                                                   current_time_after, self.ui.
                                                   time_seeker_after, 'after'))
        self.ui.time_seeker_after.valueChanged.connect(
            self.audio_player_after.setPosition)

    def save_session(self):
        if not self.sampling_rate:
            qtw.QMessageBox.information(self, 'failed',
                                        'You have to plot a signal first')
            return

        try:
            shutil.rmtree(self.PLOT_DIR)
            os.mkdir(self.PLOT_DIR)
        except FileNotFoundError:
            os.mkdir(self.PLOT_DIR)

        for index, channel in enumerate(['before', 'after']):
            exporter = pg.exporters.ImageExporter(
                self.plot_widget[channel].scene())
            exporter.export(f'{self.PLOT_DIR}/plot-{index}.png')

            self.spectrogram_widget[channel].fig.savefig(
                f'{self.PLOT_DIR}/spec-{index}.png')

        pdf = PDF()
        plots_per_page = pdf.construct(self.PLOT_DIR)

        for page_images in plots_per_page:
            pdf.print_page(page_images, self.PLOT_DIR)

        outFile = qtw.QFileDialog.getSaveFileName(None, 'Save Session', './',
                                                  "PDF File(*.pdf)")
        pdf.output(outFile[0], 'F')
        try:
            shutil.rmtree(self.PLOT_DIR)
        except:
            pass

        qtw.QMessageBox.information(self, 'success', 'PDF has been created')

        sampling_rate, samples = scipy.io.wavfile.read(
            f"wav/SBME{self.now}.wav")
        outFile = qtw.QFileDialog.getSaveFileName(None, 'Save Session', './',
                                                  "Wav File(*.wav)")
        scipy.io.wavfile.write(outFile[0], sampling_rate,
                               samples.astype(np.int16))
        qtw.QMessageBox.information(self, 'success', 'Wav has been saved')
Exemple #7
0
class Visard(QWidget, UI):
    def __init__(self):
        super().__init__()
        self.player_window()
        self.UI = False

        self.player = QMediaPlayer()
        self.player.setNotifyInterval(1)

        self.player.mediaStatusChanged.connect(self.media_status_changed)
        self.state = 0
        self.player.stateChanged.connect(self.state_changed)
        self.player.durationChanged.connect(self.duration_changed)
        self.player.positionChanged.connect(self.position_changed)
        self.player.metaDataChanged.connect(self.change_metadata)

        self.open_button.clicked.connect(self.open_dialog)

        self.volume_slider.valueChanged.connect(self.player.setVolume)

        self.position_slider.sliderPressed.connect(self.change_position_freeze)
        self.position_slider.sliderReleased.connect(
                                                  self.change_position_unfreeze)

        self.play_pause_button.clicked.connect(self.play_pause)
        self.stop_button.clicked.connect(self.stop)

    def media_status_changed(self, media_status):
        self.media_status = media_status
        if media_status == 7:
            self.player.setPosition(0)
            self.play_pause_button.setText('Play')
        print(f'MS: {media_status}!')

    def state_changed(self, state):
        self.state = state
        print(f'S: {state}!')

    def duration_changed(self, duration):
        self.duration_label.setText(ms_to_time(duration))
        self.position_slider.setMaximum(duration)
        print('DC!')

    def position_changed(self, position):
        self.position_label.setText(ms_to_time(position))
        self.position_slider.setSliderPosition(position)

    def change_metadata(self):
        artist = self.player.metaData(QMediaMetaData.ContributingArtist)
        title = self.player.metaData(QMediaMetaData.Title)
        image = self.player.metaData(QMediaMetaData.CoverArtImage)
        if artist:
            if type(artist) == list:
                artist = ', '.join(artist)
            self.artist_label.setText(artist)
        else:
            self.artist_label.setText('None')
        if title:
            self.title_label.setText(title)
        else:
            self.title_label.setText('None')
        if image:
            self.image_label.setPixmap(QPixmap.fromImage(image))
        else:
            self.image_label.setText('None')



    def open_dialog(self):
        track_directory = QFileDialog.getOpenFileName(self, 'Choose track', '',
                          'Music (*.flac *.ogg *.mp3 *.wav *.webm)')[0]
        if track_directory:
            if self.state != 0:
                if self.state == 1:
                    self.play_pause_button.setText('Play')
                self.stop_button.setEnabled(False)
            self.player.setMedia(
                             QMediaContent(QUrl.fromLocalFile(track_directory)))
            if not self.UI:
                self.artist_label.setEnabled(True)
                self.title_label.setEnabled(True)
                self.volume_slider.setEnabled(True)
                self.position_label.setEnabled(True)
                self.duration_label.setEnabled(True)
                self.position_slider.setEnabled(True)
                self.play_pause_button.setEnabled(True)
                self.UI = True

    def change_position_freeze(self):
        self.player.positionChanged.disconnect(self.position_changed)
        self.position_slider.sliderMoved.connect(self.change_position_label)

    def change_position_label(self, position):
        self.position_label.setText(ms_to_time(position))

    def change_position_unfreeze(self):
        self.player.setPosition(self.position_slider.sliderPosition())
        self.position_slider.sliderMoved.disconnect(self.change_position_label)
        self.player.positionChanged.connect(self.position_changed)

    def play_pause(self):
        if self.state == 1:
            self.player.pause()
            self.play_pause_button.setText('Play')
        else:
            if self.state == 0:
                self.stop_button.setEnabled(True)
            self.player.play()
            self.play_pause_button.setText('Pause')

    def stop(self):
        if self.state == 1:
            self.play_pause_button.setText('Play')
        self.player.stop()
        self.stop_button.setEnabled(False)
class PlaybackPanel(SpecialLabel):
    desktop_lyric_state_changed_signal = pyqtSignal(bool)
    playmode_changed_signal = pyqtSignal(int, int)
    media_player_notify_signal = pyqtSignal(int)
    muted_changed_signal = pyqtSignal(int)
    mark_favorite_completed_signal = pyqtSignal()
    current_media_changed_signal = pyqtSignal()
    music_ended_signal = pyqtSignal()
    update_window_lyric_signal = pyqtSignal(str, str)
    show_artist_info_signal = pyqtSignal(str)
    dont_hide_main_window_signal = pyqtSignal()

    def __init__(self, parent=None):
        super(PlaybackPanel, self).__init__(parent)
        self.initial_mediaplayer()
        self.create_actions()
        self.setup_ui()
        self.create_connections()
        self.initial_params()

    def create_connections(self):
        self.artistHeadLabel.clicked.connect(self.show_artist_info)
        self.desktopLyric.hide_desktop_lyric_signal.connect(self.desktop_lyric_closed)
        self.seekSlider.valueChanged.connect(self.slider_value_changed)
        self.seekSlider.sliderPressed.connect(self.slider_pressed)
        self.seekSlider.sliderReleased.connect(self.seek)
        self.mediaPlayer.positionChanged.connect(self.tick)
        self.mediaPlayer.mutedChanged.connect(self.muted_changed_signal.emit)
        self.mediaPlayer.stateChanged.connect(self.state_changed)
        self.mediaPlayer.durationChanged.connect(self.duration_changed)
        self.mediaPlayer.mediaStatusChanged.connect(self.media_status_changed)
        self.mediaPlayer.currentMediaChanged.connect(self.current_media_changed)

    def initial_mediaplayer(self):
        self.mediaPlayer = QMediaPlayer()
        self.mediaPlayer.setNotifyInterval(500)
        self.set_volume(globalSettings.Volume)

    def initial_params(self):
        self.playlist = None
        self.artistName = "Zheng-Yejian"
        self.clickPlayFlag = False  # 用来标志一首歌是否是主动点击选中的
        self.timerFlag = False
        self.timeStart = 0
        self.timeSpan = 0
        self.sourcePath = ""
        self.errorType = Configures.NoError
        self.currentSourceRow = -1
        self.nearPlayedSongs = []
        self.downloadDir = globalSettings.DownloadfilesPath
        self.songinfosManager = SonginfosManager()
        self.totalTime = Configures.ZeroTime
        self.playmode = Configures.PlaymodeRandom  # 播放模式指示器
        playlistTemp = Playlist()
        playlistTemp.fill_list(Configures.PlaylistFavorite)
        self.lovedSongs = playlistTemp.get_titles()

    def set_playlist(self, playlist):
        self.playlist = playlist
        self.currentSourceRow = self.playlist.get_current_row()

    def create_actions(self):
        self.nextAction = QAction(QIcon(IconsHub.ControlNext), "下一首", self, enabled=True, triggered=self.next_song)

        self.playAction = QAction(QIcon(IconsHub.ControlPlay), "播放/暂停", self, enabled=True, triggered=self.play_music)

        self.previousAction = QAction(
            QIcon(IconsHub.ControlPrevious), "上一首", self, enabled=True, triggered=self.previous_song
        )

        self.stopAction = QAction(
            QIcon(IconsHub.ControlStop), "停止", self, enabled=True, triggered=self.stop_music_but_timing
        )

    def get_play_button_action(self):
        return self.playAction

    def get_previous_button_action(self):
        return self.previousAction

    def get_next_button_action(self):
        return self.nextAction

    def get_stop_button_action(self):
        return self.stopAction

    def set_download_dir(self, dir):
        self.downloadDir = dir

    def get_loved_songs(self):
        return self.lovedSongs

    def get_songinfos_manager(self):
        return self.songinfosManager

    def setup_ui(self):
        self.setFixedHeight(50)
        # 桌面歌词标签
        self.desktopLyric = desktop_lyric.DesktopLyric()
        self.desktopLyric.set_color(globalSettings.DesktoplyricColors)
        # 3个标签
        self.artistHeadLabel = LabelButton()
        self.artistHeadLabel.setToolTip(self.tr("查看歌手信息"))
        self.artistHeadLabel.setFixedSize(QSize(42, 42))
        self.artistHeadLabel.setScaledContents(True)
        self.artistHeadLabel.setPixmap(QPixmap(IconsHub.Anonymous))

        self.musicTitleLabel = NewLabel()
        self.musicTitleLabel.setObjectName("musicTitleLabel")
        self.musicTitleLabel.setFixedSize(QSize(370, 20))
        self.musicTitleLabel.setText("Zheng-Yejian._.XYPLAYER")
        self.timeLabel = QLabel("00:00/00:00")
        self.timeLabel.setObjectName("timeLabel")
        self.timeLabel.setFixedHeight(20)
        self.timeLabel.setAlignment(Qt.AlignRight and Qt.AlignVCenter)

        # 五个基本按键
        self.playmodeButton = QToolButton(clicked=self.change_playmode)
        self.playmodeButton.setFocusPolicy(Qt.NoFocus)

        self.playmodeButton.setIcon(QIcon(IconsHub.PlaymodeRandom))
        self.playmodeButton.setIconSize(QSize(25, 25))
        self.playmodeButton.setToolTip("随机播放")

        self.favoriteButton = QToolButton(clicked=self.mark_as_favorite)
        self.favoriteButton.setFocusPolicy(Qt.NoFocus)
        self.favoriteButton.setToolTip("收藏")
        self.favoriteButton.setIcon(QIcon(IconsHub.Favorites))
        self.favoriteButton.setIconSize(QSize(20, 20))

        self.previousButton = QToolButton()
        self.previousButton.setFocusPolicy(Qt.NoFocus)
        self.previousButton.setIconSize(QSize(40, 40))
        self.previousButton.setShortcut(QKeySequence("Ctrl + Left"))
        self.previousButton.setDefaultAction(self.previousAction)

        self.playButton = QToolButton()
        self.playButton.setFocusPolicy(Qt.NoFocus)
        self.playButton.setIconSize(QSize(40, 40))
        self.playButton.setShortcut(QKeySequence("Ctrl + Down"))
        self.playButton.setDefaultAction(self.playAction)

        self.nextButton = QToolButton()
        self.nextButton.setFocusPolicy(Qt.NoFocus)
        self.nextButton.setIconSize(QSize(40, 40))
        self.nextButton.setFocusPolicy(Qt.NoFocus)
        self.nextButton.setShortcut(QKeySequence("Ctrl + Right"))
        self.nextButton.setDefaultAction(self.nextAction)

        self.desktopLyricButton = QToolButton(clicked=self.show_desktop_lyric)
        self.desktopLyricButton.setToolTip(self.tr("桌面歌词"))
        self.desktopLyricButton.setFocusPolicy(Qt.NoFocus)
        self.desktopLyricButton.setIcon(QIcon(IconsHub.DesktopLyric))
        self.desktopLyricButton.setIconSize(QSize(25, 25))

        self.seekSlider = QSlider(Qt.Horizontal)
        self.seekSlider.setObjectName("seekSlider")
        self.seekSlider.setFixedHeight(20)
        self.seekSlider.setFocusPolicy(Qt.NoFocus)
        self.seekSlider.setRange(0, 0)

        hbox1 = QHBoxLayout()
        hbox1.addWidget(self.favoriteButton)
        hbox1.addWidget(self.musicTitleLabel)
        hbox1.addStretch()
        hbox1.addWidget(self.timeLabel)
        vbox1 = QVBoxLayout()
        vbox1.addLayout(hbox1)
        vbox1.setSpacing(5)
        vbox1.addWidget(self.seekSlider)

        mainLayout = QHBoxLayout(self)
        mainLayout.setContentsMargins(2, 0, 0, 0)
        mainLayout.addWidget(self.artistHeadLabel)
        mainLayout.addWidget(self.previousButton)
        mainLayout.addWidget(self.playButton)
        mainLayout.addWidget(self.nextButton)
        mainLayout.addLayout(vbox1)
        mainLayout.addWidget(self.playmodeButton)
        mainLayout.addWidget(self.desktopLyricButton)

    def show_desktop_lyric(self):
        if self.desktopLyric.isHidden():
            beToOff = True
            self.desktopLyric.show()
            self.desktopLyric.original_place()
        else:
            beToOff = False
            self.desktopLyric.hide()
        self.desktop_lyric_state_changed_signal.emit(beToOff)

    def desktop_lyric_closed(self):
        self.desktop_lyric_state_changed_signal.emit(False)

    def change_playmode(self):
        oldPlaymode = self.playmode
        if self.playmode == Configures.PlaymodeRandom:
            self.set_new_playmode(Configures.PlaymodeOrder)
        elif self.playmode == Configures.PlaymodeOrder:
            self.set_new_playmode(Configures.PlaymodeSingle)
        elif self.playmode == Configures.PlaymodeSingle:
            self.set_new_playmode(Configures.PlaymodeRandom)
        self.playmode_changed_signal.emit(oldPlaymode, self.playmode)

    def set_new_playmode(self, playmode):
        self.playmode = playmode
        if playmode == Configures.PlaymodeRandom:
            iconPath = IconsHub.PlaymodeRandom
            toolTip = Configures.PlaymodeRandomText
        elif playmode == Configures.PlaymodeOrder:
            iconPath = IconsHub.PlaymodeOrder
            toolTip = Configures.PlaymodeOrderText
        else:
            iconPath = IconsHub.PlaymodeSingle
            toolTip = Configures.PlaymodeSingleText
        self.playmodeButton.setIcon(QIcon(iconPath))
        self.playmodeButton.setToolTip(toolTip)

    def ui_initial(self):
        self.mediaPlayer.stop()
        self.totalTime = "00:00"
        self.playAction.setIcon(QIcon(IconsHub.ControlPlay))
        self.musicTitleLabel.setText("Zheng-Yejian._.XYPLAYER")
        self.artistName = "Zheng-Yejian"
        self.artistHeadLabel.setPixmap(QPixmap(IconsHub.Anonymous))
        self.seekSlider.setRange(0, 0)
        self.favoriteButton.setIcon(QIcon(IconsHub.Favorites))
        self.favoriteButton.setToolTip("收藏")
        self.timeLabel.setText("00:00/00:00")

    def set_volume(self, volume):
        self.mediaPlayer.setVolume(volume)

    def set_muted(self, muted):
        self.mediaPlayer.setMuted(muted)

    def tick(self):
        currentTime = self.mediaPlayer.position()
        self.seekSlider.setValue(currentTime)
        cTime = format_position_to_mmss(currentTime // 1000)
        self.timeLabel.setText(cTime + "/" + self.totalTime)
        self.media_player_notify_signal.emit(currentTime)

    def slider_value_changed(self, value):
        cTime = format_position_to_mmss(value // 1000)
        self.timeLabel.setText("%s/%s" % (cTime, self.totalTime))
        self.media_player_notify_signal.emit(value)

    def slider_pressed(self):
        self.mediaPlayer.positionChanged.disconnect(self.tick)

    def seek(self):
        if self.mediaPlayer.state() == QMediaPlayer.StoppedState:
            self.mediaPlayer.play()
            self.mediaPlayer.setPosition(self.seekSlider.value())
        else:
            self.mediaPlayer.setPosition(self.seekSlider.value())
            self.mediaPlayer.play()
        self.mediaPlayer.positionChanged.connect(self.tick)

    def duration_changed(self, duration):
        self.seekSlider.setMaximum(duration)
        exactTotalTime = format_position_to_mmss(self.mediaPlayer.duration() // 1000)
        self.timeLabel.setText("%s/%s" % (Configures.ZeroTime, exactTotalTime))
        if self.totalTime != exactTotalTime:
            self.totalTime = exactTotalTime
            self.playlist.set_music_time_at(self.currentSourceRow, exactTotalTime)

    def check_favorite(self):
        if self.currentSourceRow >= 0:
            if self.playlist.get_music_title_at(self.currentSourceRow) in self.lovedSongs:
                self.favoriteButton.setIcon(QIcon(IconsHub.Favorites))
                self.favoriteButton.setToolTip("取消收藏")
            else:
                self.favoriteButton.setIcon(QIcon(IconsHub.FavoritesNo))
                self.favoriteButton.setToolTip("收藏")
            if self.playlist.get_name() == Configures.PlaylistFavorite:
                self.favoriteButton.setToolTip("收藏")

    def mark_as_favorite(self):
        if (
            self.playlist.get_name() == Configures.PlaylistFavorite
            or not self.playlist.length()
            or self.currentSourceRow < 0
        ):
            return
        path = self.playlist.get_music_path_at(self.currentSourceRow)
        title = self.playlist.get_music_title_at(self.currentSourceRow)
        if self.playlist.get_name() == Configures.PlaylistOnline:
            musicName = get_full_music_name_from_title(title)
            musicPath = os.path.join(self.downloadDir, musicName)
            musicPathO = os.path.join(Configures.MusicsDir, musicName)
            if not os.path.exists(musicPath) and not os.path.exists(musicPathO):
                QMessageBox.information(self, "提示", "请先下载该歌曲再添加喜欢!")
                return
            if os.path.exists(musicPath):
                path = musicPath
            else:
                path = musicPathO
        elif not os.path.exists(path):
            QMessageBox.information(self, "提示", "路径'" + "%s" % path + "'无效,无法标记喜欢!")
            return
        playlistTemp = Playlist()
        playlistTemp.fill_list(Configures.PlaylistFavorite)
        if title in self.lovedSongs:
            playlistTemp.remove_item_at(self.lovedSongs.index(title))
            playlistTemp.commit_records()
            self.lovedSongs.remove(title)
            self.favoriteButton.setIcon(QIcon(IconsHub.FavoritesNo))
            self.favoriteButton.setToolTip("收藏")
        else:
            playlistTemp.add_item_from_path(path)
            playlistTemp.commit_records()
            self.lovedSongs.append(title)
            self.favoriteButton.setIcon(QIcon(IconsHub.Favorites))
            self.favoriteButton.setToolTip("取消收藏")
        self.mark_favorite_completed_signal.emit()

    def show_artist_info(self):
        if self.artistName:
            self.show_artist_info_signal.emit(self.artistName)

    def decide_to_play_or_pause(self, row):
        if row != self.currentSourceRow:
            self.set_media_source_at_row(row, clickPlayFlag=True)
        elif self.mediaPlayer.state() in (QMediaPlayer.PausedState, QMediaPlayer.StoppedState):
            self.mediaPlayer.play()
        elif self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()

    def set_media_source_at_row(self, row, clickPlayFlag=False):
        if not self.playlist.length() or row < 0:
            return
        self.stop_music()
        self.clickPlayFlag = clickPlayFlag
        self.playlist.set_current_row(row)
        sourcePath = self.playlist.get_music_path_at(row)
        self.title = self.playlist.get_music_title_at(row)
        self.sourceTrace = "local" if self.playlist.get_music_id_at(row) == Configures.LocalMusicId else "online"
        self.artistName, self.musicName = get_artist_and_musicname_from_title(self.title)
        self.playlistName = self.playlist.get_name()
        self.totalTime = self.playlist.get_music_time_at(row)
        self.album = self.playlist.get_music_album_at(row)
        self.errorType = Configures.NoError
        isAnUrl = False
        if not os.path.exists(sourcePath):
            if self.playlist.get_name() == Configures.PlaylistOnline:
                if sourcePath == Configures.NoLink:
                    musicId = self.playlist.get_music_id_at(row)
                    sourcePath = SearchOnline.get_song_link(musicId)
                    if sourcePath:
                        self.playlist.set_music_path_at(row, sourcePath)
                    else:
                        self.errorType = Configures.UrlError
                isAnUrl = True
            else:
                self.errorType = Configures.PathError
                sourcePath = "/usr/share/sounds/error_happened.ogg"
        if self.errorType == Configures.NoError:
            self.sourcePath = sourcePath
            self.musicFileName = get_base_name_from_path(sourcePath)
            self.playedDate = get_time_of_now()
            self.songinfosManager.update_datas_of_item(
                self.musicFileName,
                self.playedDate,
                self.musicName,
                self.artistName,
                self.totalTime,
                self.album,
                self.playlistName,
            )
            if not self.timerFlag:
                self.timerFlag = True
                self.timeSpan = 0
            if isAnUrl:
                url = QUrl(sourcePath)
            else:
                url = QUrl.fromLocalFile(sourcePath)
            self.play_from_url(url)
        else:
            self.timerFlag = False
            self.dont_hide_main_window_signal.emit()
            if self.errorType == Configures.DisnetError:
                QMessageBox.critical(
                    self, "错误", "联网出错!\n无法联网播放歌曲'%s'!\n您最好在网络畅通时下载该曲目!" % self.playlist.get_music_title_at(row)
                )
            elif self.errorType == Configures.PathError:
                QMessageBox.information(self, "提示", "路径'%s'无效,请尝试重新下载并添加对应歌曲!" % self.playlist.get_music_path_at(row))

    def play_from_url(self, url):
        mediaContent = QMediaContent(url)
        self.mediaPlayer.setMedia(mediaContent)
        self.mediaPlayer.play()

    def state_changed(self, newState):
        if self and newState in [QMediaPlayer.PlayingState, QMediaPlayer.PausedState, QMediaPlayer.StoppedState]:
            if not self.playlist.length():
                return
            iconPath = IconsHub.ControlPause
            if newState in [QMediaPlayer.StoppedState, QMediaPlayer.PausedState]:
                iconPath = IconsHub.ControlPlay
            icon = QIcon(iconPath)
            self.playAction.setIcon(icon)
            if self.timerFlag:
                if newState == QMediaPlayer.PlayingState:
                    self.timeStart = time.time()
                else:
                    self.timeSpan += time.time() - self.timeStart

    def media_status_changed(self, status):
        if status == QMediaPlayer.EndOfMedia:
            self.music_finished()

    def music_finished(self):
        if self.errorType == Configures.NoError:
            self.next_song()

    def play_music(self):
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()
        else:
            self.mediaPlayer.play()

    def stop_music_but_timing(self):
        self.mediaPlayer.stop()
        self.seekSlider.setValue(0)
        self.media_player_notify_signal.emit(-0.5)

    def stop_music(self):
        self.stop_music_but_timing()
        if self.timerFlag:
            self.timerFlag = False
            InfosList = [
                self.playedDate,
                self.musicFileName,
                self.musicName,
                self.artistName,
                self.album,
                "%i" % change_mmss_to_seconds(self.totalTime),
                "%.1f" % self.timeSpan,
                self.playlistName,
                self.sourcePath,
                "%i" % (self.title in self.lovedSongs),
                self.sourceTrace,
                Configures.Playmodes[self.playmode],
                "%i" % self.clickPlayFlag,
            ]
            log_playback_history(organized_list_as_str(InfosList))
            self.songinfosManager.update_time_span_relate_of_item(self.musicFileName, self.timeSpan, self.clickPlayFlag)
            self.clickPlayFlag = False

    def get_next_random_row(self):
        listTemp = list(self.playlist.get_ids() - set(self.nearPlayedSongs))
        ran = random.randint(0, len(listTemp) - 1)
        return self.playlist.get_items_queue().index(listTemp[ran])

    def get_next_single_row(self):
        nextRow = self.currentSourceRow
        if nextRow < 0:
            nextRow = 0
        return nextRow

    def get_next_order_row(self, reverse=False):
        if reverse:
            if self.currentSourceRow < 0:
                self.currentSourceRow = 0
            return (self.currentSourceRow - 1) % self.playlist.length()
        return (self.currentSourceRow + 1) % self.playlist.length()

    def previous_song(self):
        self.play_source_on_next_row(reverse=True)

    def next_song(self):
        self.play_source_on_next_row()

    def play_source_on_next_row(self, reverse=False):
        if not self.playlist.length():
            return
        if self.mediaPlayer.position() > 20:
            self.music_ended_signal.emit()
        nextRow = 0
        if self.playmode == Configures.PlaymodeRandom:
            nextRow = self.get_next_random_row()
        elif self.playmode == Configures.PlaymodeOrder:
            nextRow = self.get_next_order_row(reverse)
        elif self.playmode == Configures.PlaymodeSingle:
            nextRow = self.get_next_single_row()
        self.set_media_source_at_row(nextRow)

    def current_media_changed(self):
        if not self.playlist.length():
            return
        self.current_media_changed_signal.emit()
        self.update_parameters()
        self.update_near_played_queue()
        self.check_favorite()

    def update_parameters(self):
        self.currentSourceRow = self.playlist.get_current_row()
        self.musicTitleLabel.setText(self.title)
        self.playAction.setText(self.musicName)
        imagePath = SearchOnline.get_artist_image_path(self.artistName)
        if imagePath:
            pixmap = QPixmap(imagePath)
        else:
            pixmap = QPixmap(IconsHub.Anonymous)
        self.artistHeadLabel.setPixmap(pixmap)
        musicId = self.playlist.get_music_id_at(self.currentSourceRow)
        self.update_window_lyric_signal.emit(self.title, musicId)

    def update_near_played_queue(self):
        self.currentSourceId = self.playlist.get_music_path_at(self.currentSourceRow)
        if self.playlist.get_name() == Configures.PlaylistOnline:
            self.currentSourceId = self.playlist.get_music_id_at(self.currentSourceRow)
        if self.currentSourceId not in self.nearPlayedSongs:
            self.nearPlayedSongs.append(self.currentSourceId)
        while len(self.nearPlayedSongs) >= self.playlist.length() * 4 / 5:
            del self.nearPlayedSongs[0]

    def add_title_into_loved_songs(self, title):
        self.lovedSongs.append(title)
class PlaybackPanel(SpecialLabel):
    desktop_lyric_state_changed_signal = pyqtSignal(bool)
    playmode_changed_signal = pyqtSignal(int, int)
    media_player_notify_signal = pyqtSignal(int)
    muted_changed_signal = pyqtSignal(int)
    mark_favorite_completed_signal = pyqtSignal()
    current_media_changed_signal = pyqtSignal()
    music_ended_signal = pyqtSignal()
    update_window_lyric_signal = pyqtSignal(str, str)
    show_artist_info_signal = pyqtSignal(str)
    dont_hide_main_window_signal = pyqtSignal()

    def __init__(self, parent=None):
        super(PlaybackPanel, self).__init__(parent)
        self.initial_mediaplayer()
        self.create_actions()
        self.setup_ui()
        self.create_connections()
        self.initial_params()

    def create_connections(self):
        self.artistHeadLabel.clicked.connect(self.show_artist_info)
        self.desktopLyric.hide_desktop_lyric_signal.connect(
            self.desktop_lyric_closed)
        self.seekSlider.valueChanged.connect(self.slider_value_changed)
        self.seekSlider.sliderPressed.connect(self.slider_pressed)
        self.seekSlider.sliderReleased.connect(self.seek)
        self.mediaPlayer.positionChanged.connect(self.tick)
        self.mediaPlayer.mutedChanged.connect(self.muted_changed_signal.emit)
        self.mediaPlayer.stateChanged.connect(self.state_changed)
        self.mediaPlayer.durationChanged.connect(self.duration_changed)
        self.mediaPlayer.mediaStatusChanged.connect(self.media_status_changed)
        self.mediaPlayer.currentMediaChanged.connect(
            self.current_media_changed)

    def initial_mediaplayer(self):
        self.mediaPlayer = QMediaPlayer()
        self.mediaPlayer.setNotifyInterval(500)
        self.set_volume(globalSettings.Volume)

    def initial_params(self):
        self.playlist = None
        self.artistName = 'Zheng-Yejian'
        self.clickPlayFlag = False  #用来标志一首歌是否是主动点击选中的
        self.timerFlag = False
        self.timeStart = 0
        self.timeSpan = 0
        self.sourcePath = ''
        self.errorType = Configures.NoError
        self.currentSourceRow = -1
        self.nearPlayedSongs = []
        self.downloadDir = globalSettings.DownloadfilesPath
        self.songinfosManager = SonginfosManager()
        self.totalTime = Configures.ZeroTime
        self.playmode = Configures.PlaymodeRandom  #播放模式指示器
        playlistTemp = Playlist()
        playlistTemp.fill_list(Configures.PlaylistFavorite)
        self.lovedSongs = playlistTemp.get_titles()

    def set_playlist(self, playlist):
        self.playlist = playlist
        self.currentSourceRow = self.playlist.get_current_row()

    def create_actions(self):
        self.nextAction = QAction(QIcon(IconsHub.ControlNext),
                                  "下一首",
                                  self,
                                  enabled=True,
                                  triggered=self.next_song)

        self.playAction = QAction(QIcon(IconsHub.ControlPlay),
                                  "播放/暂停",
                                  self,
                                  enabled=True,
                                  triggered=self.play_music)

        self.previousAction = QAction(QIcon(IconsHub.ControlPrevious),
                                      "上一首",
                                      self,
                                      enabled=True,
                                      triggered=self.previous_song)

        self.stopAction = QAction(QIcon(IconsHub.ControlStop),
                                  "停止",
                                  self,
                                  enabled=True,
                                  triggered=self.stop_music_but_timing)

    def get_play_button_action(self):
        return self.playAction

    def get_previous_button_action(self):
        return self.previousAction

    def get_next_button_action(self):
        return self.nextAction

    def get_stop_button_action(self):
        return self.stopAction

    def set_download_dir(self, dir):
        self.downloadDir = dir

    def get_loved_songs(self):
        return self.lovedSongs

    def get_songinfos_manager(self):
        return self.songinfosManager

    def setup_ui(self):
        self.setFixedHeight(50)
        #桌面歌词标签
        self.desktopLyric = desktop_lyric.DesktopLyric()
        self.desktopLyric.set_color(globalSettings.DesktoplyricColors)
        #3个标签
        self.artistHeadLabel = LabelButton()
        self.artistHeadLabel.setToolTip(self.tr('查看歌手信息'))
        self.artistHeadLabel.setFixedSize(QSize(42, 42))
        self.artistHeadLabel.setScaledContents(True)
        self.artistHeadLabel.setPixmap(QPixmap(IconsHub.Anonymous))

        self.musicTitleLabel = NewLabel()
        self.musicTitleLabel.setObjectName('musicTitleLabel')
        self.musicTitleLabel.setFixedSize(QSize(370, 20))
        self.musicTitleLabel.setText("Zheng-Yejian._.XYPLAYER")
        self.timeLabel = QLabel("00:00/00:00")
        self.timeLabel.setObjectName('timeLabel')
        self.timeLabel.setFixedHeight(20)
        self.timeLabel.setAlignment(Qt.AlignRight and Qt.AlignVCenter)

        #五个基本按键
        self.playmodeButton = QToolButton(clicked=self.change_playmode)
        self.playmodeButton.setFocusPolicy(Qt.NoFocus)

        self.playmodeButton.setIcon(QIcon(IconsHub.PlaymodeRandom))
        self.playmodeButton.setIconSize(QSize(25, 25))
        self.playmodeButton.setToolTip('随机播放')

        self.favoriteButton = QToolButton(clicked=self.mark_as_favorite)
        self.favoriteButton.setFocusPolicy(Qt.NoFocus)
        self.favoriteButton.setToolTip('收藏')
        self.favoriteButton.setIcon(QIcon(IconsHub.Favorites))
        self.favoriteButton.setIconSize(QSize(20, 20))

        self.previousButton = QToolButton()
        self.previousButton.setFocusPolicy(Qt.NoFocus)
        self.previousButton.setIconSize(QSize(40, 40))
        self.previousButton.setShortcut(QKeySequence("Ctrl + Left"))
        self.previousButton.setDefaultAction(self.previousAction)

        self.playButton = QToolButton()
        self.playButton.setFocusPolicy(Qt.NoFocus)
        self.playButton.setIconSize(QSize(40, 40))
        self.playButton.setShortcut(QKeySequence("Ctrl + Down"))
        self.playButton.setDefaultAction(self.playAction)

        self.nextButton = QToolButton()
        self.nextButton.setFocusPolicy(Qt.NoFocus)
        self.nextButton.setIconSize(QSize(40, 40))
        self.nextButton.setFocusPolicy(Qt.NoFocus)
        self.nextButton.setShortcut(QKeySequence("Ctrl + Right"))
        self.nextButton.setDefaultAction(self.nextAction)

        self.desktopLyricButton = QToolButton(clicked=self.show_desktop_lyric)
        self.desktopLyricButton.setToolTip(self.tr("桌面歌词"))
        self.desktopLyricButton.setFocusPolicy(Qt.NoFocus)
        self.desktopLyricButton.setIcon(QIcon(IconsHub.DesktopLyric))
        self.desktopLyricButton.setIconSize(QSize(25, 25))

        self.seekSlider = QSlider(Qt.Horizontal)
        self.seekSlider.setObjectName('seekSlider')
        self.seekSlider.setFixedHeight(20)
        self.seekSlider.setFocusPolicy(Qt.NoFocus)
        self.seekSlider.setRange(0, 0)

        hbox1 = QHBoxLayout()
        hbox1.addWidget(self.favoriteButton)
        hbox1.addWidget(self.musicTitleLabel)
        hbox1.addStretch()
        hbox1.addWidget(self.timeLabel)
        vbox1 = QVBoxLayout()
        vbox1.addLayout(hbox1)
        vbox1.setSpacing(5)
        vbox1.addWidget(self.seekSlider)

        mainLayout = QHBoxLayout(self)
        mainLayout.setContentsMargins(2, 0, 0, 0)
        mainLayout.addWidget(self.artistHeadLabel)
        mainLayout.addWidget(self.previousButton)
        mainLayout.addWidget(self.playButton)
        mainLayout.addWidget(self.nextButton)
        mainLayout.addLayout(vbox1)
        mainLayout.addWidget(self.playmodeButton)
        mainLayout.addWidget(self.desktopLyricButton)

    def show_desktop_lyric(self):
        if self.desktopLyric.isHidden():
            beToOff = True
            self.desktopLyric.show()
            self.desktopLyric.original_place()
        else:
            beToOff = False
            self.desktopLyric.hide()
        self.desktop_lyric_state_changed_signal.emit(beToOff)

    def desktop_lyric_closed(self):
        self.desktop_lyric_state_changed_signal.emit(False)

    def change_playmode(self):
        oldPlaymode = self.playmode
        if self.playmode == Configures.PlaymodeRandom:
            self.set_new_playmode(Configures.PlaymodeOrder)
        elif self.playmode == Configures.PlaymodeOrder:
            self.set_new_playmode(Configures.PlaymodeSingle)
        elif self.playmode == Configures.PlaymodeSingle:
            self.set_new_playmode(Configures.PlaymodeRandom)
        self.playmode_changed_signal.emit(oldPlaymode, self.playmode)

    def set_new_playmode(self, playmode):
        self.playmode = playmode
        if playmode == Configures.PlaymodeRandom:
            iconPath = IconsHub.PlaymodeRandom
            toolTip = Configures.PlaymodeRandomText
        elif playmode == Configures.PlaymodeOrder:
            iconPath = IconsHub.PlaymodeOrder
            toolTip = Configures.PlaymodeOrderText
        else:
            iconPath = IconsHub.PlaymodeSingle
            toolTip = Configures.PlaymodeSingleText
        self.playmodeButton.setIcon(QIcon(iconPath))
        self.playmodeButton.setToolTip(toolTip)

    def ui_initial(self):
        self.mediaPlayer.stop()
        self.totalTime = '00:00'
        self.playAction.setIcon(QIcon(IconsHub.ControlPlay))
        self.musicTitleLabel.setText("Zheng-Yejian._.XYPLAYER")
        self.artistName = 'Zheng-Yejian'
        self.artistHeadLabel.setPixmap(QPixmap(IconsHub.Anonymous))
        self.seekSlider.setRange(0, 0)
        self.favoriteButton.setIcon(QIcon(IconsHub.Favorites))
        self.favoriteButton.setToolTip('收藏')
        self.timeLabel.setText("00:00/00:00")

    def set_volume(self, volume):
        self.mediaPlayer.setVolume(volume)

    def set_muted(self, muted):
        self.mediaPlayer.setMuted(muted)

    def tick(self):
        currentTime = self.mediaPlayer.position()
        self.seekSlider.setValue(currentTime)
        cTime = format_position_to_mmss(currentTime // 1000)
        self.timeLabel.setText(cTime + '/' + self.totalTime)
        self.media_player_notify_signal.emit(currentTime)

    def slider_value_changed(self, value):
        cTime = format_position_to_mmss(value // 1000)
        self.timeLabel.setText('%s/%s' % (cTime, self.totalTime))
        self.media_player_notify_signal.emit(value)

    def slider_pressed(self):
        self.mediaPlayer.positionChanged.disconnect(self.tick)

    def seek(self):
        if self.mediaPlayer.state() == QMediaPlayer.StoppedState:
            self.mediaPlayer.play()
            self.mediaPlayer.setPosition(self.seekSlider.value())
        else:
            self.mediaPlayer.setPosition(self.seekSlider.value())
            self.mediaPlayer.play()
        self.mediaPlayer.positionChanged.connect(self.tick)

    def duration_changed(self, duration):
        self.seekSlider.setMaximum(duration)
        exactTotalTime = format_position_to_mmss(self.mediaPlayer.duration() //
                                                 1000)
        self.timeLabel.setText('%s/%s' % (Configures.ZeroTime, exactTotalTime))
        if self.totalTime != exactTotalTime:
            self.totalTime = exactTotalTime
            self.playlist.set_music_time_at(self.currentSourceRow,
                                            exactTotalTime)

    def check_favorite(self):
        if self.currentSourceRow >= 0:
            if self.playlist.get_music_title_at(
                    self.currentSourceRow) in self.lovedSongs:
                self.favoriteButton.setIcon(QIcon(IconsHub.Favorites))
                self.favoriteButton.setToolTip('取消收藏')
            else:
                self.favoriteButton.setIcon(QIcon(IconsHub.FavoritesNo))
                self.favoriteButton.setToolTip('收藏')
            if self.playlist.get_name() == Configures.PlaylistFavorite:
                self.favoriteButton.setToolTip('收藏')

    def mark_as_favorite(self):
        if self.playlist.get_name(
        ) == Configures.PlaylistFavorite or not self.playlist.length(
        ) or self.currentSourceRow < 0:
            return
        path = self.playlist.get_music_path_at(self.currentSourceRow)
        title = self.playlist.get_music_title_at(self.currentSourceRow)
        if self.playlist.get_name() == Configures.PlaylistOnline:
            musicName = get_full_music_name_from_title(title)
            musicPath = os.path.join(self.downloadDir, musicName)
            musicPathO = os.path.join(Configures.MusicsDir, musicName)
            if not os.path.exists(musicPath) and not os.path.exists(
                    musicPathO):
                QMessageBox.information(self, '提示', '请先下载该歌曲再添加喜欢!')
                return
            if os.path.exists(musicPath):
                path = musicPath
            else:
                path = musicPathO
        elif not os.path.exists(path):
            QMessageBox.information(self, "提示",
                                    "路径'" + "%s" % path + "'无效,无法标记喜欢!")
            return
        playlistTemp = Playlist()
        playlistTemp.fill_list(Configures.PlaylistFavorite)
        if title in self.lovedSongs:
            playlistTemp.remove_item_at(self.lovedSongs.index(title))
            playlistTemp.commit_records()
            self.lovedSongs.remove(title)
            self.favoriteButton.setIcon(QIcon(IconsHub.FavoritesNo))
            self.favoriteButton.setToolTip("收藏")
        else:
            playlistTemp.add_item_from_path(path)
            playlistTemp.commit_records()
            self.lovedSongs.append(title)
            self.favoriteButton.setIcon(QIcon(IconsHub.Favorites))
            self.favoriteButton.setToolTip("取消收藏")
        self.mark_favorite_completed_signal.emit()

    def show_artist_info(self):
        if self.artistName:
            self.show_artist_info_signal.emit(self.artistName)

    def decide_to_play_or_pause(self, row):
        if row != self.currentSourceRow:
            self.set_media_source_at_row(row, clickPlayFlag=True)
        elif self.mediaPlayer.state() in (QMediaPlayer.PausedState,
                                          QMediaPlayer.StoppedState):
            self.mediaPlayer.play()
        elif self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()

    def set_media_source_at_row(self, row, clickPlayFlag=False):
        if not self.playlist.length() or row < 0:
            return
        self.stop_music()
        self.clickPlayFlag = clickPlayFlag
        self.playlist.set_current_row(row)
        sourcePath = self.playlist.get_music_path_at(row)
        self.title = self.playlist.get_music_title_at(row)
        self.sourceTrace = 'local' if self.playlist.get_music_id_at(
            row) == Configures.LocalMusicId else 'online'
        self.artistName, self.musicName = get_artist_and_musicname_from_title(
            self.title)
        self.playlistName = self.playlist.get_name()
        self.totalTime = self.playlist.get_music_time_at(row)
        self.album = self.playlist.get_music_album_at(row)
        self.errorType = Configures.NoError
        isAnUrl = False
        if not os.path.exists(sourcePath):
            if self.playlist.get_name() == Configures.PlaylistOnline:
                if sourcePath == Configures.NoLink:
                    musicId = self.playlist.get_music_id_at(row)
                    sourcePath = SearchOnline.get_song_link(musicId)
                    if sourcePath:
                        self.playlist.set_music_path_at(row, sourcePath)
                    else:
                        self.errorType = Configures.UrlError
                isAnUrl = True
            else:
                self.errorType = Configures.PathError
                sourcePath = "/usr/share/sounds/error_happened.ogg"
        if self.errorType == Configures.NoError:
            self.sourcePath = sourcePath
            self.musicFileName = get_base_name_from_path(sourcePath)
            self.playedDate = get_time_of_now()
            self.songinfosManager.update_datas_of_item(
                self.musicFileName, self.playedDate, self.musicName,
                self.artistName, self.totalTime, self.album, self.playlistName)
            if not self.timerFlag:
                self.timerFlag = True
                self.timeSpan = 0
            if isAnUrl:
                url = QUrl(sourcePath)
            else:
                url = QUrl.fromLocalFile(sourcePath)
            self.play_from_url(url)
        else:
            self.timerFlag = False
            self.dont_hide_main_window_signal.emit()
            if self.errorType == Configures.DisnetError:
                QMessageBox.critical(
                    self, "错误", "联网出错!\n无法联网播放歌曲'%s'!\n您最好在网络畅通时下载该曲目!" %
                    self.playlist.get_music_title_at(row))
            elif self.errorType == Configures.PathError:
                QMessageBox.information(
                    self, "提示", "路径'%s'无效,请尝试重新下载并添加对应歌曲!" %
                    self.playlist.get_music_path_at(row))

    def play_from_url(self, url):
        mediaContent = QMediaContent(url)
        self.mediaPlayer.setMedia(mediaContent)
        self.mediaPlayer.play()

    def state_changed(self, newState):
        if self and newState in [
                QMediaPlayer.PlayingState, QMediaPlayer.PausedState,
                QMediaPlayer.StoppedState
        ]:
            if not self.playlist.length():
                return
            iconPath = IconsHub.ControlPause
            if newState in [
                    QMediaPlayer.StoppedState, QMediaPlayer.PausedState
            ]:
                iconPath = IconsHub.ControlPlay
            icon = QIcon(iconPath)
            self.playAction.setIcon(icon)
            if self.timerFlag:
                if newState == QMediaPlayer.PlayingState:
                    self.timeStart = time.time()
                else:
                    self.timeSpan += (time.time() - self.timeStart)

    def media_status_changed(self, status):
        if status == QMediaPlayer.EndOfMedia:
            self.music_finished()

    def music_finished(self):
        if self.errorType == Configures.NoError:
            self.next_song()

    def play_music(self):
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()
        else:
            self.mediaPlayer.play()

    def stop_music_but_timing(self):
        self.mediaPlayer.stop()
        self.seekSlider.setValue(0)
        self.media_player_notify_signal.emit(-0.5)

    def stop_music(self):
        self.stop_music_but_timing()
        if self.timerFlag:
            self.timerFlag = False
            InfosList = [
                self.playedDate, self.musicFileName, self.musicName,
                self.artistName, self.album,
                '%i' % change_mmss_to_seconds(self.totalTime),
                '%.1f' % self.timeSpan, self.playlistName, self.sourcePath,
                '%i' % (self.title in self.lovedSongs), self.sourceTrace,
                Configures.Playmodes[self.playmode],
                '%i' % self.clickPlayFlag
            ]
            log_playback_history(organized_list_as_str(InfosList))
            self.songinfosManager.update_time_span_relate_of_item(
                self.musicFileName, self.timeSpan, self.clickPlayFlag)
            self.clickPlayFlag = False

    def get_next_random_row(self):
        listTemp = list(self.playlist.get_ids() - set(self.nearPlayedSongs))
        ran = random.randint(0, len(listTemp) - 1)
        return self.playlist.get_items_queue().index(listTemp[ran])

    def get_next_single_row(self):
        nextRow = self.currentSourceRow
        if nextRow < 0:
            nextRow = 0
        return nextRow

    def get_next_order_row(self, reverse=False):
        if reverse:
            if self.currentSourceRow < 0:
                self.currentSourceRow = 0
            return (self.currentSourceRow - 1) % self.playlist.length()
        return (self.currentSourceRow + 1) % self.playlist.length()

    def previous_song(self):
        self.play_source_on_next_row(reverse=True)

    def next_song(self):
        self.play_source_on_next_row()

    def play_source_on_next_row(self, reverse=False):
        if not self.playlist.length():
            return
        if self.mediaPlayer.position() > 20:
            self.music_ended_signal.emit()
        nextRow = 0
        if self.playmode == Configures.PlaymodeRandom:
            nextRow = self.get_next_random_row()
        elif self.playmode == Configures.PlaymodeOrder:
            nextRow = self.get_next_order_row(reverse)
        elif self.playmode == Configures.PlaymodeSingle:
            nextRow = self.get_next_single_row()
        self.set_media_source_at_row(nextRow)

    def current_media_changed(self):
        if not self.playlist.length():
            return
        self.current_media_changed_signal.emit()
        self.update_parameters()
        self.update_near_played_queue()
        self.check_favorite()

    def update_parameters(self):
        self.currentSourceRow = self.playlist.get_current_row()
        self.musicTitleLabel.setText(self.title)
        self.playAction.setText(self.musicName)
        imagePath = SearchOnline.get_artist_image_path(self.artistName)
        if imagePath:
            pixmap = QPixmap(imagePath)
        else:
            pixmap = QPixmap(IconsHub.Anonymous)
        self.artistHeadLabel.setPixmap(pixmap)
        musicId = self.playlist.get_music_id_at(self.currentSourceRow)
        self.update_window_lyric_signal.emit(self.title, musicId)

    def update_near_played_queue(self):
        self.currentSourceId = self.playlist.get_music_path_at(
            self.currentSourceRow)
        if self.playlist.get_name() == Configures.PlaylistOnline:
            self.currentSourceId = self.playlist.get_music_id_at(
                self.currentSourceRow)
        if self.currentSourceId not in self.nearPlayedSongs:
            self.nearPlayedSongs.append(self.currentSourceId)
        while len(self.nearPlayedSongs) >= self.playlist.length() * 4 / 5:
            del self.nearPlayedSongs[0]

    def add_title_into_loved_songs(self, title):
        self.lovedSongs.append(title)
Exemple #10
0
class ApplicationWindow(QtWidgets.QMainWindow):

    # >> QtMultimedia Signals
    #----------------------
    play = pyqtSignal()
    pause = pyqtSignal()
    stop = pyqtSignal()

    def __init__(self):

        QtWidgets.QMainWindow.__init__(self)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

        self.main_widget = QtWidgets.QWidget(self)
        audioGlobals.playerStarted = False

        #DEFINE PLAYER-PLAYLIST   
        #----------------------
        self.source = QtCore.QUrl.fromLocalFile(os.path.abspath(audioGlobals.wavFileName))
        self.content = QMediaContent(self.source)
        self.player = QMediaPlayer()
        self.playlist = QMediaPlaylist(self)
        self.playlist.addMedia(self.content)
        self.player.setPlaylist(self.playlist)

        # >> Define annotations and gantt chart 
        #---------------------- 
        self.wave = vA.Waveform()
        audioGlobals.fig = self.wave
        self.chart = gA.Chart()
        audioGlobals.chartFig = self.chart

        # >> Define player buttons 
        #---------------------- 
        playButton = QPushButton("Play")
        pauseButton = QPushButton("Pause")
        stopButton = QPushButton("Stop")

        # >> Define layouts 
        #---------------------- 
        waveLayout = QVBoxLayout()
        waveLayout.addWidget(self.wave)
        waveLayout.addWidget(self.chart)

        line = QFrame()
        line.setFrameShape(QFrame.VLine)
        line.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Expanding)
        waveLayout.addWidget(line)

        #Buttons layout
        buttonLayout = QVBoxLayout()
        buttonLayout.addWidget(playButton)
        buttonLayout.addWidget(pauseButton)
        buttonLayout.addWidget(stopButton)
        buttonLayout.setAlignment(Qt.AlignTop)


        # >> Specify final layout align 
        #----------------------
        layout = QHBoxLayout(self.main_widget)
        layout.addLayout(waveLayout)
        layout.addLayout(buttonLayout)
        
        # >> Define buttons connections 
        #---------------------- 
        playButton.clicked.connect(self.Play)
        pauseButton.clicked.connect(self.Pause)
        stopButton.clicked.connect(self.Stop)


        self.main_widget.setFocus()
        self.setCentralWidget(self.main_widget)


    # PLAYER BUTTON FUNCTIONS

    # >> Play audio (whole signal or segment) 
    #---------------------- 
    def Play(self):

        #GET CLICKS FROM WAVEFORM
        #---------------------- 
        #Initialize connection-position ONCE
        if not audioGlobals.playerStarted:
            #10ms for changePosition -> Not Delaying
            self.player.positionChanged.connect(self.checkPositionToStop)
            self.player.setNotifyInterval(10)
            if audioGlobals.durationFlag==0:
                audioGlobals.playerStarted = True
                audioGlobals.startTimeToPlay = 0
                self.start = audioGlobals.startTimeToPlay
                self.end = audioGlobals.duration*1000 - 10
                audioGlobals.endTimeToPlay = self.end
                audioGlobals.counterClick = 3
            elif audioGlobals.durationFlag==1:
                audioGlobals.playerStarted = True
                self.start = audioGlobals.startTimeToPlay
                self.end = audioGlobals.duration*1000 - 10
                audioGlobals.endTimeToPlay = self.end
                audioGlobals.counterClick = 3
            elif audioGlobals.durationFlag==2:
                audioGlobals.playerStarted = True
                self.start = audioGlobals.startTimeToPlay
                self.end = audioGlobals.endTimeToPlay
            self.player.setPosition(self.start)

        playFlag = True
        self.player.play()

    # >> Pause audio playing 
    #----------------------
    def Pause(self):
        #Not begging from self.start
        audioGlobals.playerStarted = True
        self.player.setPosition(self.time_)
        self.player.pause()

    # >> Stop audio playing 
    #----------------------
    def Stop(self):
        self.player.stop()
        #Begin again segment
        self.start = audioGlobals.startTimeToPlay
        self.player.setPosition(self.start)

    # >> Check ms in audio to stop play 
    #----------------------
    def checkPositionToStop(self):
        self.time_ = self.player.position()
        tStart = float(self.time)/1000.0
        iS = np.argmin(np.abs(audioGlobals.timeArrayToPlot - tStart))
        audioGlobals.fig.axes.plot(audioGlobals.timeArrayToPlot[iS],self.signalToPlot[iS], color = 'black', alpha=0.65)
        print self.time_
        if self.time_ >= self.end:
            self.Stop()
            self.player.setPosition(self.start)

    def fileQuit(self):
        self.close()

    def closeEvent(self, ce):
        self.fileQuit()
Exemple #11
0
class QmyMainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)  #调用父类构造函数,创建窗体
        self.ui = Ui_MainWindow()  #创建UI对象
        self.ui.setupUi(self)  #构造UI界面

        self.player = QMediaPlayer(self)  #创建视频播放器
        self.player.setNotifyInterval(1000)  #信息更新周期, ms

        scene = QGraphicsScene(self)
        self.ui.graphicsView.setScene(scene)

        self.videoItem = QGraphicsVideoItem()  #视频显示画面
        self.videoItem.setSize(QSizeF(320, 220))
        self.videoItem.setFlag(QGraphicsItem.ItemIsMovable)
        self.videoItem.setFlag(QGraphicsItem.ItemIsSelectable)
        self.videoItem.setFlag(QGraphicsItem.ItemIsFocusable)

        scene.addItem(self.videoItem)
        self.player.setVideoOutput(self.videoItem)  #设置视频显示图形项

        self.textItem = QGraphicsTextItem("面朝大海,春暖花开")  #弹幕文字
        font = self.textItem.font()
        font.setPointSize(20)
        self.textItem.setFont(font)
        self.textItem.setDefaultTextColor(Qt.red)
        self.textItem.setPos(100, 220)
        self.textItem.setFlag(QGraphicsItem.ItemIsMovable)
        self.textItem.setFlag(QGraphicsItem.ItemIsSelectable)
        self.textItem.setFlag(QGraphicsItem.ItemIsFocusable)
        scene.addItem(self.textItem)

        self.ui.btnText.setCheckable(True)  #弹幕文字按钮
        self.ui.btnText.setChecked(True)

        self.__duration = ""
        self.__curPos = ""
        self.player.stateChanged.connect(self.do_stateChanged)
        self.player.positionChanged.connect(self.do_positionChanged)
        self.player.durationChanged.connect(self.do_durationChanged)

##  ==============自定义功能函数========================

##  ==============event处理函数==========================

    def closeEvent(self, event):  #窗体关闭时
        # 窗口关闭时不能自动停止播放,需手动停止
        if (self.player.state() == QMediaPlayer.PlayingState):
            self.player.stop()

##  ==========由connectSlotsByName()自动连接的槽函数============

    @pyqtSlot()  ##打开文件
    def on_btnOpen_clicked(self):
        curPath = QDir.currentPath()  #获取系统当前目录
        ##      curPath=os.getcwd()
        title = "选择视频文件"
        filt = "视频文件(*.wmv *.avi);;所有文件(*.*)"
        fileName, flt = QFileDialog.getOpenFileName(self, title, curPath, filt)

        if (fileName == ""):
            return

        fileInfo = QFileInfo(fileName)
        baseName = fileInfo.fileName()
        ##      baseName=os.path.basename(fileName)
        self.ui.LabCurMedia.setText(baseName)
        curPath = fileInfo.absolutePath()
        QDir.setCurrent(curPath)  #重设当前目录

        media = QMediaContent(QUrl.fromLocalFile(fileName))

        self.player.setMedia(media)  #设置播放文件
        self.player.play()

    @pyqtSlot()  ##播放
    def on_btnPlay_clicked(self):
        self.player.play()

    @pyqtSlot()  ##暂停
    def on_btnPause_clicked(self):
        self.player.pause()

    @pyqtSlot()  ##停止
    def on_btnStop_clicked(self):
        self.player.stop()

    @pyqtSlot()  ##全屏
    def on_btnFullScreen_clicked(self):
        self.videoWidget.setFullScreen(True)

    @pyqtSlot()  ##静音按钮
    def on_btnSound_clicked(self):
        mute = self.player.isMuted()
        self.player.setMuted(not mute)
        if mute:
            self.ui.btnSound.setIcon(QIcon(":/icons/images/volumn.bmp"))
        else:
            self.ui.btnSound.setIcon(QIcon(":/icons/images/mute.bmp"))

    @pyqtSlot(int)  ##音量调节
    def on_sliderVolumn_valueChanged(self, value):
        self.player.setVolume(value)

    @pyqtSlot(int)  ##播放进度调节
    def on_sliderPosition_valueChanged(self, value):
        self.player.setPosition(value)

    @pyqtSlot()  ##放大
    def on_btnZoomIn_clicked(self):
        sc = self.videoItem.scale()
        self.videoItem.setScale(sc + 0.1)

    @pyqtSlot()  ##缩小
    def on_btnZoomOut_clicked(self):
        sc = self.videoItem.scale()
        self.videoItem.setScale(sc - 0.1)

    @pyqtSlot(bool)  ##弹幕
    def on_btnText_clicked(self, checked):
        self.textItem.setVisible(checked)

##  =============自定义槽函数===============================

    def do_stateChanged(self, state):
        isPlaying = (state == QMediaPlayer.PlayingState)

        self.ui.btnPlay.setEnabled(not isPlaying)
        self.ui.btnPause.setEnabled(isPlaying)
        self.ui.btnStop.setEnabled(isPlaying)

    def do_durationChanged(self, duration):
        self.ui.sliderPosition.setMaximum(duration)

        secs = duration / 1000  #秒
        mins = secs / 60  #分钟
        secs = secs % 60  #余数秒
        self.__duration = "%d:%d" % (mins, secs)
        self.ui.LabRatio.setText(self.__curPos + "/" + self.__duration)

    def do_positionChanged(self, position):
        if (self.ui.sliderPosition.isSliderDown()):
            return  #如果正在拖动滑条,退出

        self.ui.sliderPosition.setSliderPosition(position)

        secs = position / 1000  #秒
        mins = secs / 60  #分钟
        secs = secs % 60  #余数秒
        self.__curPos = "%d:%d" % (mins, secs)
        self.ui.LabRatio.setText(self.__curPos + "/" + self.__duration)
class VideoEditor(QFrame):
    """
    This widget allows to load a video file and zoom onto a specific ROI (region of interest)
    """

    meta_data_loaded = pyqtSignal(dict)

    @property
    def is_playing(self):
        """
        True if the video is currently being played.
        """
        return self._media_player.state() == QMediaPlayer.PlayingState

    @property
    def duration(self):
        """
        Duration in seconds of the video clip currently loaded.
        """
        return self._duration

    @property
    def start_trim(self):
        """
        Gets the duration of the trimmed footage at the beginning of the video clip in seconds.
        """
        return self._start_trim

    @start_trim.setter
    def start_trim(self, value):
        """
        Sets how much of the beginning of the clip should be trimmed.
        :param value: Footage to cut in seconds
        """
        if not self.video_file_open:
            return
        if value > self.end_trim:
            value = self.end_trim
        if value < 0:
            value = 0

        self._start_trim = value
        self._start_frame = int(value * self.fps)
        self._timeline.setMinimum(value * 1000)
        self._current_time.setText('{:10.3f}'.format(
            round(self._timeline.value() / self.fps / 1000 - self._start_trim,
                  3)))
        self._total_time.setText('{:10.3f}'.format(self._end_trim -
                                                   self._start_trim))

    @property
    def end_trim(self):
        """
        Gets the time in seconds until which the video clip is shown. The rest is trimmed.
        """
        return self._end_trim

    @end_trim.setter
    def end_trim(self, value):
        """
        Sets the time in seconds until which the video clip is displayed. Everything beyond is trimmed.
        :param value: Time code in seconds until which the video clip shoud be shown
        """
        if not self.video_file_open:
            return
        if value > self._duration_time_code / 1000:
            value = self._duration_time_code / 1000
        if value < self._start_trim:
            value = self._start_trim

        self._end_trim = value
        self._end_frame = int(value * self.fps)
        self._timeline.setMaximum(value * 1000)
        self._current_time.setText('{:10.3f}'.format(
            round(self._timeline.value() / self.fps / 1000 - self._start_trim,
                  3)))
        self._total_time.setText('{:10.3f}'.format(self._end_trim -
                                                   self._start_trim))

    @property
    def fps(self):
        """
        Frames per second of the currently loaded video clip.
        """
        return self._fps

    @property
    def frame_count(self):
        """
        Total amount of frames of the currently loaded video clip.
        """
        return self._frame_count

    @property
    def current_frame(self):
        """
        Gets the current frame
        """
        return self._current_frame

    @current_frame.setter
    def current_frame(self, value):
        """
        Sets the current frame
        :param value: frame number
        """
        self._current_frame = value
        self._allow_frame_counter_update = False
        self._media_player.setPosition(self._frame_number_to_time_code(value))
        self._allow_frame_counter_update = True

    @property
    def start_frame(self):
        """
        Gets the start frame. (Depends on the trimming of the video clip)
        """
        return self._start_frame

    @property
    def end_frame(self):
        """
        Gets the end frame. (Depends on the trimming of the video clip)
        """
        return self._end_frame

    @property
    def video_width(self):
        """
        Horizontal resolution of the currently loaded video clip.
        """
        return self._video_width

    @property
    def video_height(self):
        """
        Vertical resolution of the currently loaded video clip.
        """
        return self._video_height

    @property
    def video_path(self):
        """
        Path of the currently loaded video file.
        """
        return self._video_path

    @property
    def video_file_open(self):
        """
        True if a video file is currently opened in the editor.
        """
        return self._video_file_open

    @property
    def roi(self):
        """
        Returns a QRect representing the current region of interest. (If None, the entire image is the ROI)
        """
        return self._roi

    @property
    def overlay_layers(self):
        return self._image_control.overlay_layers

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.installEventFilter(
            self)  # start listening for mouse events to capture ROI changes
        self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored
                           )  # make the editor use as much space as possible
        self._media_player = QMediaPlayer(self, QMediaPlayer.VideoSurface)
        self._media_player.positionChanged.connect(self._player_time_changed_)
        self._media_player.metaDataAvailableChanged.connect(
            self._meta_data_changed_)
        self._grabber = VideoFrameGrabber(self)
        self._allow_frame_counter_update = True
        self._result_frame = None
        self._media_player.setVideoOutput(self._grabber)
        self._grabber.frameAvailable.connect(self._frame_ready_)
        self._video_file_open = False
        self._video_path = None
        self._display_image = dict(
        )  # cache for images that are displayed from the video
        self._frame_count = 0
        self._duration_time_code = 0
        self._duration = 0
        self._fps = 1
        self._current_frame = 0
        self._video_width = 0
        self._video_height = 0
        self._file_path = None
        self._is_playing = False
        self._roi = None
        self._start_trim = 0
        self._end_trim = 0
        self._start_frame = 0
        self._end_frame = 0
        self._layers = list(
        )  # layers for visualizations on top of the video footage
        self._selection_layer = ImageLayer(
            enabled=False)  # layer for selection indicators
        self._selection_rectangle = ImageRectangle(
            0, 0, filled=False, border_color=Qt.yellow)  # selection rectangle
        self._selection_layer.shapes.append(self._selection_rectangle)
        self._selection_start = None  # start point for selection rectangle
        self.time_code_changed = list()

        self._layout = QGridLayout()
        self._timeline = None
        self._current_time = None
        self._total_time = None
        self._frame_box = None
        self._play_button = None
        self._stop_button = None
        self._next_frame_button = None
        self._previous_frame_button = None
        self._box_roi_x = None
        self._box_roi_y = None
        self._box_roi_width = None
        self._box_roi_height = None
        self._accept_roi_updates_from_boxes = True
        self._wait = False

        self.setLayout(self._layout)
        self._setup_()

    def _time_code_to_frame_number_(self, time_code):
        """
        Convert time code to frame number
        :param time_code: time code (in ms)
        :return: frame number
        """
        return int(time_code / 1000 * self.fps)

    def _frame_number_to_time_code(self, f_number):
        """
        Convert frame number to time code
        :param f_number: frame number
        :return: time code (in ms)
        """
        return int(f_number / self.fps * 1000)

    def _to_image_space_(self, point: QPoint):
        """
        Convert a point on the editor widget in to a point in the video footage.
        :param point: point in coordinates of the widget
        :return: point in the coordinates of the video footage
        """
        control_position = self._image_control.pos(
        )  # get the position of the video image on the editor
        control_size = self._image_control.size(
        )  # get the size of the video image
        dx = (control_size.width() - self._image_control.image_width
              ) / 2  # get the x offset of the footage in the image
        dy = (control_size.height() - self._image_control.image_height
              ) / 2  # get the y offset of the footage in the image
        x = (point.x() - dx -
             control_position.x()) / self._image_control.image_scale_x
        y = (point.y() - dy -
             control_position.y()) / self._image_control.image_scale_y
        return QPoint(x, y)

    def eventFilter(self, obj, event):
        """
        Check for mouse events to edit the ROI
        :param obj: object that caused the event
        :param event: event parameters (i.e. mouse position on the widget)
        """
        if event.type(
        ) == QEvent.MouseMove:  # if the mouse was moved, update the selection size
            target = self._to_image_space_(event.pos())
            self._selection_rectangle.position = self._selection_start
            self._selection_rectangle.width = target.x(
            ) - self._selection_start.x()
            self._selection_rectangle.height = target.y(
            ) - self._selection_start.y()
            self._image_control.update()
        elif event.type(
        ) == QEvent.MouseButtonPress:  # if the left mouse button was pressed designate the point as start of the selection
            self._selection_layer.enabled = True
            target = self._to_image_space_(event.pos())
            self._selection_start = target
        elif event.type(
        ) == QEvent.MouseButtonRelease and self._selection_start is not None:  # if the button was release the the ROI
            self._selection_layer.enabled = False
            end_point = self._to_image_space_(event.pos())

            # get all possible corner points
            x1 = self._selection_start.x()
            x2 = end_point.x()
            y1 = self._selection_start.y()
            y2 = end_point.y()

            # find upper left corner of the ROI
            roi_x = x1 if x1 < x2 else x2
            roi_y = y1 if y1 < y2 else y2

            # find extent of the ROI
            roi_width = abs(x1 - x2)
            roi_height = abs(y1 - y2)

            # set the ROI if it was not just a click with no extent
            if roi_width > 0 and roi_height > 0:
                # take into account if the footage was already focused onto a previous ROI
                if self._roi is not None:
                    roi_x += self._roi.x()
                    roi_y += self._roi.y()

                # update spin box values
                self._accept_roi_updates_from_boxes = False  # disable ROI changes from the spin boxes
                self._box_roi_x.setValue(roi_x)
                self._box_roi_y.setValue(roi_y)
                self._box_roi_width.setValue(roi_width)
                self._box_roi_height.setValue(roi_height)
                self._accept_roi_updates_from_boxes = True  # enable ROI changes from the spin boxes
                self.set_roi(QRect(roi_x, roi_y, roi_width,
                                   roi_height))  # set ROI
                self._selection_start = None  # remove selection start
        return False

    def sizeHint(self):
        """
        Needed for widget to expand properly on the UI (Should be improved)
        """
        return QSize(1200, 1200)

    def load(self, path):
        """
        Load a video file from the given path
        :param path: path of the file
        """
        if self.video_file_open:  # close current video file if one was open
            self.close()

        self._media_player.setMedia(QMediaContent(QUrl.fromLocalFile(path)))
        self._video_file_open = True
        self._media_player.pause()

    def _player_time_changed_(self, position):
        self._timeline.setValueSilent(position)
        if self._allow_frame_counter_update:
            self._current_frame = self._time_code_to_frame_number_(position)

        # used for updating keyframes
        self._image_control.current_frame = self._current_frame

        # update the UI
        self._current_time.setText('{:10.3f}'.format(
            round(position / 1000 - self._start_trim, 3)))
        self._frame_box.setText('(frame: {:04})'.format(self.current_frame))

        # send event about frame change
        for callback in self.time_code_changed:
            callback(position / 1000 - self._start_trim)

    def _meta_data_changed_(self, available):
        if self._media_player.isMetaDataAvailable():
            resolution = self._media_player.metaData('Resolution')

            self._duration_time_code = self._media_player.metaData(
                'Duration')  # get duration in ms
            self._fps = self._media_player.metaData(
                'VideoFrameRate')  # get frames per second of the video
            self._media_player.setNotifyInterval(1000 / self._fps)
            self._frame_count = self._time_code_to_frame_number_(
                self._duration_time_code)  # get total video frames
            self._video_width = resolution.width()  # get width of the image
            self._video_height = resolution.height()  # get height of the image
            self.start_trim = 0  # no trimming when video is loaded
            self._start_frame = 0  # first frame is also the first frame of the video
            self._duration = self._duration_time_code / 1000
            self._end_frame = self._frame_count - 1  # don't trim the end of the video
            self.end_trim = self.duration  # use the duration of the video as trim mark (no trimming)

            # set maximum values of the ROI spin boxes
            self._box_roi_x.setMaximum(self._video_width)
            self._box_roi_width.setMaximum(self._video_width)
            self._box_roi_y.setMaximum(self._video_height)
            self._box_roi_height.setMaximum(self._video_height)

            # update the data on the UI elements
            self._total_time.setText('{:10.3f}'.format(self._end_trim -
                                                       self._start_trim))
            self._frame_box.setText('(Frame: 0000)')
            self._timeline.setValue(0)
            self._timeline.setMaximum(self._duration_time_code)
            self._timeline.setEnabled(True)
            self._play_button.setEnabled(True)
            self._stop_button.setEnabled(True)
            self._next_frame_button.setEnabled(True)
            self._previous_frame_button.setEnabled(True)
            self.reset_roi()

            meta_data = dict()
            for key in self._media_player.availableMetaData():
                meta_data[key] = self._media_player.metaData(key)
            self.meta_data_loaded.emit(meta_data)

    def _frame_ready_(self, frame):
        if self._roi is not None:  # crop the frame to the ROI if one has been specified
            self._result_frame = frame.copy(self._roi)
        else:
            self._result_frame = frame
        self._image_control.set_image(self._result_frame)
        self._wait = False

        if self._media_player.position() > self.end_trim * 1000:
            self.pause()
            self._media_player.setPosition(self.end_trim * 1000)
        elif self._media_player.position() < self.start_trim * 1000:
            self.pause()
            self._media_player.setPosition(self.start_trim * 1000)

    def set_time(self, seconds):
        """
        Display the frame that is the closest to the given time
        :param seconds: time at which to display the frame in seconds
        """
        if not self.video_file_open:
            return
        self._media_player.setPosition(seconds * 1000)

    def get_time(self, frame_number):
        """
        Return the time code at the specified frame
        :param frame_number: frame number
        :return: time code in seconds
        """
        if not self.video_file_open:  # return zero if no file is opened
            return 0
        frame_number = self._clamp_frame_number_(frame_number)
        return frame_number / self.fps - self._start_trim

    @staticmethod
    def _to_numpy_array_(frame):
        channels = int(frame.byteCount() / frame.width() / frame.height())
        bits = frame.bits()
        bits.setsize(frame.byteCount())
        return np.frombuffer(bits, np.uint8).reshape(frame.height(),
                                                     frame.width(), channels)

    def get_frame(self, frame_number, wait_for_new_frame=False):
        """
        Returns a numpy array containing the video frame at the given frame number
        :param frame_number: frame number
        :param wait_for_new_frame: Makes sure that a new frame is retrieved before it is returned (otherwise most recent is returned)
        :return: numpy array
        """
        if not self.video_file_open:  # return None if no video file is opened
            return None

        frame_number = self._clamp_frame_number_(frame_number)
        if wait_for_new_frame:
            self._wait = True
        old_interval = self._media_player.notifyInterval()
        self._media_player.setNotifyInterval(1)
        self._media_player.setPosition(
            self._frame_number_to_time_code(frame_number))
        while self._wait:
            sleep(0.001)
        self._media_player.setNotifyInterval(old_interval)

        result_frame = self._result_frame
        data = self._to_numpy_array_(result_frame)
        return data

    def _clamp_frame_number_(self, frame_number):
        """
        Clamps the given frame number to an allowed range
        :param frame_number: frame number
        :return: frame number between 0 and frame_count - 1
        """
        if frame_number < self._start_frame:
            frame_number = self._start_frame
        elif frame_number > self._end_frame:
            frame_number = self._end_frame
        return int(frame_number)

    def _clamp_time_code_(self, time_code):
        """
        Clamps the given time code to an allowed range
        :param time_code: time code (in ms)
        :return: time code (in ms)
        """
        if time_code < self._start_trim * 1000:
            time_code = self._start_trim * 1000
        elif time_code > self._end_trim * 1000:
            time_code = self._end_trim * 1000
        return time_code

    def set_roi(self, rect: QRect):
        """
        Sets the region of interest on the video footage.
        :param rect: rectangle representing the region of interest
        """
        self._roi = rect
        self._box_roi_x.setValue(rect.x())
        self._box_roi_y.setValue(rect.y())
        self._box_roi_width.setValue(rect.width())
        self._box_roi_height.setValue(rect.height())

        if self._frame_count > 0:
            self._display_time_code_(
                self._timeline.value())  # update the display

    def reset_roi(self):
        """
        Reset the region of interest
        """
        self._roi = None

        # set the full image as ROI on the spin boxes
        self._accept_roi_updates_from_boxes = False  # stop the spin boxes from updating the ROI
        self._box_roi_x.setValue(0)
        self._box_roi_y.setValue(0)
        self._box_roi_width.setValue(self._video_width)
        self._box_roi_height.setValue(self._video_height)
        self._accept_roi_updates_from_boxes = True  # re-enable the spin boxes to update the ROI

        if self._frame_count > 0:
            self._display_time_code_(self._timeline.value())

    def play(self):
        """
        Start playing the video that is currently loaded.
        """
        if self.is_playing:  # do nothing if the video is already playing
            return

        self._media_player.play()
        self._play_button.setIcon(self.style().standardIcon(
            QStyle.SP_MediaPause))  # set pause button icon to pause

    def pause(self):
        """
        Pauses the video.
        """
        self._media_player.pause()
        self._play_button.setIcon(self.style().standardIcon(
            QStyle.SP_MediaPlay))  # set pause button icon to play

    def stop(self):
        """
        Stop the video and rewind.
        """
        self.pause()  # stop the video from playing
        self._media_player.setPosition(self.start_trim *
                                       1000)  # return to the first frame

    def next_frame(self):
        """
        Skip one frame ahead.
        """
        if self.current_frame < self.end_frame:
            self.current_frame += 1

    def previous_frame(self):
        """
        Skip to the previous frame.
        """
        if self.current_frame > self.start_frame:
            self.current_frame -= 1

    def _play_pause_(self):
        """
        Play if the video is paused or pause if the video is currently playing.
        """
        if self.is_playing:
            self.pause()
        else:
            self.play()

    def _display_time_code_(self, time_code):
        """
        Displays the requested time code on the widget.
        :param time_code: time code (in ms)
        """
        if not self.video_file_open:  # do nothing if no video file is open
            return

        time_code = self._clamp_time_code_(time_code)  # get proper time code
        self._media_player.setPosition(time_code)

    def _roi_box_value_changed_(self, *args):
        """
        Callback for changes made in the ROI spin boxes. Adjust the ROI accordingly.
        """
        if self._accept_roi_updates_from_boxes:
            roi_x = self._box_roi_x.value()
            roi_y = self._box_roi_y.value()
            roi_width = self._box_roi_width.value()
            roi_height = self._box_roi_height.value()
            self.set_roi(QRect(roi_x, roi_y, roi_width, roi_height))

    def close(self):
        """
        Closes the video file which is  currently opened.
        """
        if self._video_file_open:
            self._media_player.stop()
            self._frame_count = 0  # set the frame count to zero
            self._fps = 1  # set the frames per second to zero
            self._media_player.setPosition(0)  # set the timeline to zero
            self._timeline.setEnabled(False)  # disable the timeline
            self._play_button.setEnabled(False)  # disable the play button
            self._stop_button.setEnabled(False)  # disable the stop button
            self._next_frame_button.setEnabled(
                True)  # disable the skip frame button
            self._previous_frame_button.setEnabled(
                True)  # disable the previous frame button
            self._current_time.setText(
                '0.000')  # set the current time code to zero
            self._total_time.setText('0.000')  # set the total time to zero
            self._frame_box.setText(
                '(frame: 0000)')  # set the current frame to zero
            self._box_roi_x.setMaximum(0)  # set the ROI maximum to zero
            self._box_roi_width.setMaximum(0)  # set the ROI maximum to zero
            self._box_roi_y.setMaximum(0)  # set the ROI maximum to zero
            self._box_roi_height.setMaximum(0)  # set the ROI maximum to zero
            self.reset_roi()  # reset the ROI

    def _setup_(self):
        self._image_control = ImageRenderWidget()
        self._image_control.overlay_layers.append(self._selection_layer)
        self._layout.addWidget(self._image_control, 0, 0, 1, 10,
                               Qt.AlignCenter)

        self._timeline = QJumpSlider(Qt.Horizontal)
        self._timeline.setEnabled(False)
        self._timeline.valueChangedSmart.connect(self._display_time_code_)
        self._layout.addWidget(self._timeline, 1, 4)

        self._current_time = QLabel('0.000')
        self._total_time = QLabel('0.000')
        self._frame_box = QLabel('(Frame: 0000)')
        self._layout.addWidget(self._current_time, 1, 5)
        self._layout.addWidget(QLabel('/'), 1, 6)
        self._layout.addWidget(self._total_time, 1, 7)
        self._layout.addWidget(QLabel(' s'), 1, 8)
        self._layout.addWidget(self._frame_box, 1, 9)

        self._play_button = QPushButton()
        self._play_button.setIcon(self.style().standardIcon(
            QStyle.SP_MediaPlay))
        self._play_button.setEnabled(False)
        self._play_button.clicked.connect(self._play_pause_)
        self._layout.addWidget(self._play_button, 1, 0)

        self._stop_button = QPushButton()
        self._stop_button.setIcon(self.style().standardIcon(
            QStyle.SP_MediaStop))
        self._stop_button.setEnabled(False)
        self._stop_button.clicked.connect(self.stop)
        self._layout.addWidget(self._stop_button, 1, 1)

        self._next_frame_button = QPushButton()
        self._next_frame_button.setIcon(self.style().standardIcon(
            QStyle.SP_MediaSkipForward))
        self._next_frame_button.setEnabled(False)
        self._next_frame_button.clicked.connect(self.next_frame)
        self._layout.addWidget(self._next_frame_button, 1, 3)

        self._previous_frame_button = QPushButton()
        self._previous_frame_button.setIcon(self.style().standardIcon(
            QStyle.SP_MediaSkipBackward))
        self._previous_frame_button.setEnabled(False)
        self._previous_frame_button.clicked.connect(self.previous_frame)
        self._layout.addWidget(self._previous_frame_button, 1, 2)

        roi_frame = QFrame()
        roi_layout = QHBoxLayout()
        roi_frame.setLayout(roi_layout)
        roi_frame.setFixedHeight(38)
        roi_layout.addWidget(QLabel('ROI: ['))
        roi_layout.addWidget(QLabel('x:'))
        self._box_roi_x = QSpinBox()
        roi_layout.addWidget(self._box_roi_x)
        roi_layout.addWidget(QLabel('y:'))
        self._box_roi_y = QSpinBox()
        roi_layout.addWidget(self._box_roi_y)
        roi_layout.addWidget(QLabel('width:'))
        self._box_roi_width = QSpinBox()
        roi_layout.addWidget(self._box_roi_width)
        roi_layout.addWidget(QLabel('height:'))
        self._box_roi_height = QSpinBox()
        roi_layout.addWidget(self._box_roi_height)
        roi_layout.addWidget(QLabel(']'))
        roi_reset_button = QPushButton('Reset')
        roi_reset_button.clicked.connect(self.reset_roi)
        roi_layout.addWidget(roi_reset_button)
        self._box_roi_x.valueChanged.connect(self._roi_box_value_changed_)
        self._box_roi_y.valueChanged.connect(self._roi_box_value_changed_)
        self._box_roi_width.valueChanged.connect(self._roi_box_value_changed_)
        self._box_roi_height.valueChanged.connect(self._roi_box_value_changed_)
        self._layout.addWidget(roi_frame, 2, 0, 1, 9, Qt.AlignLeft)
Exemple #13
0
class Window(QWidget, Ui_Window):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        self.status = False

        self.player = QMediaPlayer()
        self.player.setNotifyInterval(100)
        self.open_button.clicked.connect(self.open_dialog)

        self.play_pause_button.clicked.connect(self.play_pause)
        self.stop_button.clicked.connect(self.stop)

        self.player.durationChanged.connect(self.track_duration)
        self.player.positionChanged.connect(self.track_position)

        self.player.mediaStatusChanged.connect(self.track_status)

        self.position_slider.sliderPressed.connect(
            self.change_track_position_block)
        self.position_slider.sliderReleased.connect(
            self.change_track_position_unblock)

        self.volume_slider.valueChanged.connect(self.player.setVolume)
        self.mute_button.clicked.connect(self.mute)

        self.playback_spinbox.valueChanged.connect(self.change_playback)

    def play_pause(self):
        if self.status:
            self.play_pause_button.setText('Play')
            self.player.pause()
            self.status = False
        else:
            self.play_pause_button.setText('Pause')
            self.stop_button.setEnabled(True)
            self.player.play()
            self.status = True

    def stop(self):
        if self.status:
            self.play_pause_button.setText('Play')
        self.stop_button.setEnabled(False)
        self.player.stop()
        self.status = False

    def track_duration(self, duration):
        self.track_duration_label.setText(ms_to_time(duration))
        self.position_slider.setMaximum(duration)

    def track_position(self, position):
        self.track_position_label.setText(ms_to_time(position))
        self.position_slider.setValue(position)

    def change_track_position_block(self):
        self.player.positionChanged.disconnect(self.track_position)
        self.position_slider.valueChanged.connect(
            self.change_track_position_label)

    def change_track_position_label(self, value):
        self.track_position_label.setText(ms_to_time(value))

    def change_track_position_unblock(self):
        self.player.setPosition(self.position_slider.value())
        self.position_slider.valueChanged.disconnect(
            self.change_track_position_label)
        self.player.positionChanged.connect(self.track_position)

    def track_status(self, status):
        if status == 7:
            self.stop()
            self.player.setPosition(0)

    def change_playback(self, value):
        self.player.positionChanged.disconnect(self.track_position)
        position = self.player.position()
        self.player.stop()
        self.player.setPlaybackRate(value)
        self.player.play()
        self.player.setPosition(position)
        self.player.positionChanged.connect(self.track_position)

    def mute(self):
        if self.player.isMuted():
            self.player.setMuted(False)
            self.volume_slider.setEnabled(True)
            self.mute_button.setText('🔊')
        else:
            self.volume_slider.setEnabled(False)
            self.player.setMuted(True)
            self.mute_button.setText('🔈')

    def open_dialog(self):
        track = QFileDialog.getOpenFileName(
            self, dialog, '', 'Music (*.flac *.ogg *.mp3 *.wav *.webm)')[0]
        if track:
            self.play_pause_button.setEnabled(True)
            self.position_slider.setEnabled(True)
            self.volume_slider.setEnabled(True)
            self.mute_button.setEnabled(True)
            self.playback_spinbox.setEnabled(True)
            self.player.setMedia(QMediaContent(QUrl.fromLocalFile(track)))
            self.stop()
Exemple #14
0
class Main(QMainWindow, Ui_MainWindow):

    lyricList = []
    lyricCount = 0
    videoPath = ""
    videoName = ""
    darkMode = False
    redMode = False
    blueMode = False
    latestStartTime = QtCore.QTime(0, 0, 0)
    latestEndTime = QtCore.QTime(0, 0, 0)

    def __init__(self):
        super(Main, self).__init__()
        self.setupUi(self)

        self.removeButton.clicked.connect(self.OnRemove)
        self.addButton.clicked.connect(lambda: self.OnAddButton())
        self.createSRTButton.clicked.connect(self.CreateSRT)
        self.actionOpen_Video.triggered.connect(self.OpenVideoFile)

        #Modes
        self.actionDark_Mode_2.triggered.connect(
            lambda: self.ToggleDarkMode(self.darkMode))
        self.actionLight_Mode.triggered.connect(self.ToggleLightMode)
        self.actionRed_Palette.triggered.connect(
            lambda: self.ToggleRedMode(self.redMode))
        self.actionBlue_Palette.triggered.connect(
            lambda: self.ToggleBlueMode(self.blueMode))

        #Video
        self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)

        btnSize = QSize(16, 16)
        videoWidget = QVideoWidget()

        openButton = QPushButton("Abrir Video")
        openButton.setToolTip("Abrir Video")
        openButton.setStatusTip("Abrir Video")
        openButton.setFixedHeight(40)
        openButton.setIconSize(btnSize)
        openButton.setFont(QFont("Noto Sans", 15))
        openButton.setIcon(
            QIcon.fromTheme("document-open", QIcon("D:/_Qt/img/open.png")))
        openButton.clicked.connect(self.open)

        self.playButton = QPushButton()
        self.playButton.setEnabled(False)
        self.playButton.setFixedHeight(65)
        self.playButton.setFixedWidth(60)
        self.playButton.setIconSize(btnSize)
        self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
        self.playButton.clicked.connect(self.play)

        self.positionSlider = QSlider(Qt.Horizontal)
        self.positionSlider.setRange(0, 0)
        self.positionSlider.sliderMoved.connect(self.setPosition)

        self.statusBar = QStatusBar()
        self.statusBar.setFont(QFont("Noto Sans", 7))
        self.statusBar.setFixedHeight(14)

        controlLayout = QHBoxLayout()
        controlLayout.setContentsMargins(0, 0, 0, 0)
        controlLayout.addWidget(openButton)
        controlLayout.addWidget(self.playButton)
        controlLayout.addWidget(self.positionSlider)

        #self.QVideoBoxVLayout = QVBoxLayout()
        self.QVideoBoxVLayout.addWidget(videoWidget)
        self.QVideoBoxVLayout.addLayout(controlLayout)
        self.QVideoBoxVLayout.addWidget(self.statusBar)

        self.setLayout(self.QVideoBoxVLayout)

        self.mediaPlayer.setVideoOutput(videoWidget)
        self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
        self.mediaPlayer.positionChanged.connect(self.positionChanged)
        self.mediaPlayer.durationChanged.connect(self.durationChanged)
        self.mediaPlayer.error.connect(self.handleError)
        # Every 200 ms
        self.mediaPlayer.setNotifyInterval(200)
        self.translator = google_translator()
        self.statusBar.showMessage("Listo")

        # connect buttons playback
        self.fiveBack.clicked.connect(lambda: self.TimeSkip(5, False))
        self.tenBack.clicked.connect(lambda: self.TimeSkip(10, False))
        self.oneMBack.clicked.connect(lambda: self.TimeSkip(.1, False))
        self.fiveForward.clicked.connect(lambda: self.TimeSkip(5, True))
        self.tenForward.clicked.connect(lambda: self.TimeSkip(10, True))
        self.oneMForward.clicked.connect(lambda: self.TimeSkip(.1, True))

        #Import srt
        self.actionInportar_Subtitulos_srt.triggered.connect(self.ImportSRT)

    def ToggleLightMode(self):
        light_palette = QPalette()
        appctxt.app.setPalette(light_palette)

    def ToggleRedMode(self, dark):

        if not dark:
            red_palette = QPalette()

            red_palette.setColor(QPalette.Window, QColor(100, 53, 53))
            red_palette.setColor(QPalette.WindowText, Qt.white)
            red_palette.setColor(QPalette.Base, QColor(25, 25, 25))
            red_palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53))
            red_palette.setColor(QPalette.ToolTipBase, Qt.white)
            red_palette.setColor(QPalette.ToolTipText, Qt.white)
            red_palette.setColor(QPalette.Text, Qt.white)
            red_palette.setColor(QPalette.Button, QColor(53, 53, 53))
            red_palette.setColor(QPalette.ButtonText, Qt.white)
            red_palette.setColor(QPalette.BrightText, Qt.red)
            red_palette.setColor(QPalette.Link, QColor(42, 130, 218))
            red_palette.setColor(QPalette.Highlight, QColor(42, 130, 218))
            red_palette.setColor(QPalette.HighlightedText, Qt.black)

            appctxt.app.setPalette(red_palette)
            #appctxt.app.setStyleSheet(
            #    "QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }")
            self.redMode = True
        else:
            light_palette = QPalette()
            appctxt.app.setPalette(light_palette)
            self.redMode = False

    def ToggleBlueMode(self, dark):

        if not dark:
            blue_palette = QPalette()

            blue_palette.setColor(QPalette.Window, QColor(53, 53, 100))
            blue_palette.setColor(QPalette.WindowText, Qt.white)
            blue_palette.setColor(QPalette.Base, QColor(25, 25, 25))
            blue_palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53))
            blue_palette.setColor(QPalette.ToolTipBase, Qt.white)
            blue_palette.setColor(QPalette.ToolTipText, Qt.white)
            blue_palette.setColor(QPalette.Text, Qt.white)
            blue_palette.setColor(QPalette.Button, QColor(53, 53, 53))
            blue_palette.setColor(QPalette.ButtonText, Qt.white)
            blue_palette.setColor(QPalette.BrightText, Qt.red)
            blue_palette.setColor(QPalette.Link, QColor(42, 130, 218))
            blue_palette.setColor(QPalette.Highlight, QColor(42, 130, 218))
            blue_palette.setColor(QPalette.HighlightedText, Qt.black)

            appctxt.app.setPalette(blue_palette)
            #appctxt.app.setStyleSheet(
            #    "QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }")
            self.blueMode = True
        else:
            light_palette = QPalette()
            appctxt.app.setPalette(light_palette)
            self.blueMode = False

    def ToggleDarkMode(self, dark):

        if not dark:
            dark_palette = QPalette()

            dark_palette.setColor(QPalette.Window, QColor(53, 53, 53))
            dark_palette.setColor(QPalette.WindowText, Qt.white)
            dark_palette.setColor(QPalette.Base, QColor(25, 25, 25))
            dark_palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53))
            dark_palette.setColor(QPalette.ToolTipBase, Qt.white)
            dark_palette.setColor(QPalette.ToolTipText, Qt.white)
            dark_palette.setColor(QPalette.Text, Qt.white)
            dark_palette.setColor(QPalette.Button, QColor(53, 53, 53))
            dark_palette.setColor(QPalette.ButtonText, Qt.white)
            dark_palette.setColor(QPalette.BrightText, Qt.red)
            dark_palette.setColor(QPalette.Link, QColor(42, 130, 218))
            dark_palette.setColor(QPalette.Highlight, QColor(42, 130, 218))
            dark_palette.setColor(QPalette.HighlightedText, Qt.black)

            appctxt.app.setPalette(dark_palette)
            #appctxt.app.setStyleSheet(
            #    "QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }")
            self.darkMode = True
        else:
            light_palette = QPalette()
            appctxt.app.setPalette(light_palette)
            self.darkMode = False

    # Currently only supports mp4
    def ImportSRT(self):
        fileName, _ = QFileDialog.getOpenFileName(self,
                                                  "Selecciona los mediose",
                                                  ".", "SRT Files (*.srt)")
        videoFilePath = (fileName.split(".")[0]) + ".mp4"
        print(videoFilePath)
        if fileName != '':
            self.mediaPlayer.setMedia(
                QMediaContent(QUrl.fromLocalFile(videoFilePath)))
            self.playButton.setEnabled(True)
            self.statusBar.showMessage(videoFilePath)
            self.play()
            self.videoPath = videoFilePath
            self.videoName = self.videoPath.split("/")[-1]

            # Delete existing objects
            count = self.lyricCount
            for i in range(count):
                self.OnRemove()

            # Create video objects
            self.CreateLyricObjectsFromSRT(fileName)

    def CreateLyricObjectsFromSRT(self, path):
        srtFile = open(path, "r", encoding="utf-8")

        # General srt format is
        # index
        # start time --> end time
        # lyrics
        # empty line

        #--- Temp variables
        index = 1
        foundStart = False
        foundEnd = False
        foundLyrics = False
        lyricsString = ""
        subStringTime = "-->"
        startString = ""
        endString = ""
        # iterate through srt file
        for line in srtFile:
            # after times is lyrics
            if foundStart and foundEnd and not foundLyrics:
                # until we find empty
                if line == "\n":
                    foundLyrics = True

                if lyricsString != "":
                    lyricsString = lyricsString + line.strip('\n').rstrip()
                else:
                    lyricsString = line
            # times
            if subStringTime in line:
                startString = line.split(" --> ")[0].rstrip()
                endString = line.split(" --> ")[-1].rstrip()
                if startString != "":
                    foundStart = True
                else:
                    print("No start time for item", index)
                if endString != "":
                    foundEnd = True
                else:
                    print("No end time for item ", index)
            index = index + 1
            if foundLyrics and startString != "" and endString != "":
                #Create lyrics object here

                startQTime = self.ReturnQTimeObject(startString)
                endQtime = self.ReturnQTimeObject(endString)

                self.OnAdd(start=startQTime, end=endQtime, lyrics=lyricsString)
                lyricsString = ""
                foundLyrics = False
                foundStart = False
                foundEnd = False

        srtFile.close()

    def ReturnQTimeObject(self, timeString):
        hours = timeString.split(":")[0]
        minutes = timeString.split(":")[1]
        secondsAndMsec = timeString.split(":")[2]
        seconds = secondsAndMsec.split(",")[0]
        mSeconds = secondsAndMsec.split(",")[1]

        return QtCore.QTime(int(hours), int(minutes), int(seconds),
                            int(mSeconds))

    def TimeSkip(self, amount, forward):
        if forward:
            tempPosition = self.mediaPlayer.position()
            self.setPosition(tempPosition + int(amount * 1000))
        else:
            tempPosition = self.mediaPlayer.position()
            self.setPosition(tempPosition - int(amount * 1000))

    def SetCurrentTimeText(self, millis):
        millis = int(millis)
        seconds = (millis / 1000) % 60
        seconds = int(seconds)
        minutes = (millis / (1000 * 60)) % 60
        minutes = int(minutes)

        sMillis = self.ThreeCharSyntax(str(millis))
        sSeconds = self.TwoCharSyntax(str(seconds))
        sMinutes = self.TwoCharSyntax(str(minutes))

        self.currentTime.setText("Tiempo Actual " + sMinutes + ":" + sSeconds +
                                 ":" + sMillis)

    def open(self):
        fileName, _ = QFileDialog.getOpenFileName(
            self, "Selecciona los mediose", ".",
            "Video Files (*.mp4 *.flv *.ts *.mts *.avi)")

        if fileName != '':
            self.mediaPlayer.setMedia(
                QMediaContent(QUrl.fromLocalFile(fileName)))
            self.playButton.setEnabled(True)
            self.statusBar.showMessage(fileName)
            self.play()
            self.videoPath = fileName
            self.videoName = self.videoPath.split("/")[-1]

    def OpenVideoFile(self):
        #self.videoPath, _ = QFileDialog.getOpenFileName(self, 'Open File', options=QFileDialog.DontUseNativeDialog)

        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        self.videoPath, _ = QFileDialog.getOpenFileName(
            self,
            "Select Video File",
            "",
            "Video File (*.mp4 *.avi *.ogv)",
            options=options)
        self.videoName = self.videoPath.split("/")[-1]

    def play(self):
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()
        else:
            self.mediaPlayer.play()

    def mediaStateChanged(self, state):
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.playButton.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPause))
            # Show exact time when paused
            self.SetCurrentTimeText(self.mediaPlayer.position())
            self.SetCurrentLyrics(self.mediaPlayer.position())
        else:
            self.playButton.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPlay))

    def positionChanged(self, position):
        self.positionSlider.setValue(position)
        self.SetCurrentTimeText(self.mediaPlayer.position())
        self.SetCurrentLyrics(position)

    def durationChanged(self, duration):
        self.positionSlider.setRange(0, duration)

    def setPosition(self, position):
        self.mediaPlayer.setPosition(position)

    def handleError(self):
        self.playButton.setEnabled(False)
        self.statusBar.showMessage("Error: " + self.mediaPlayer.errorString())

    def OnRemove(self):
        if len(self.lyricList) > 0:
            self.verticalLayout.removeWidget(self.lyricList[-1])
            sip.delete(self.lyricList[-1])
            self.lyricCount -= 1
            del (self.lyricList[-1])
            self.progressBar.setProperty("value", 0)

    def OnAddButton(self):
        if self.lyricCount > 0:

            endTimeObject = self.lyricList[self.lyricCount - 1].findChild(
                QtWidgets.QTimeEdit, "endTime").time()
            newTime = QtCore.QTime(0, endTimeObject.minute(),
                                   endTimeObject.second(),
                                   endTimeObject.msec() + 1)
            self.OnAdd(start=newTime, end=newTime)
            self.scrollArea.ensureWidgetVisible(self.lyricGroup)
            max = self.scrollArea.verticalScrollBar().maximum()
            self.scrollArea.verticalScrollBar().setValue(999999)

        else:
            self.OnAdd()
            max = self.scrollArea.verticalScrollBar().maximum()
            self.scrollArea.verticalScrollBar().setValue(max)

    def OnAdd(self,
              start=QtCore.QTime(0, 0, 0),
              end=QtCore.QTime(0, 0, 0),
              lyrics=""):
        self.lyricGroup = QtWidgets.QWidget(self.scrollAreaWidgetContents)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum,
                                           QtWidgets.QSizePolicy.Maximum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.lyricGroup.sizePolicy().hasHeightForWidth())
        self.lyricGroup.setSizePolicy(sizePolicy)
        self.lyricGroup.setMinimumSize(QtCore.QSize(0, 150))
        self.lyricGroup.setMaximumSize(QtCore.QSize(600, 100))
        self.lyricGroup.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.lyricGroup.setObjectName("lyricGroup")

        self.gridLayout = QtWidgets.QGridLayout(self.lyricGroup)
        self.gridLayout.setObjectName("gridLayout")
        self.startLabel = QtWidgets.QLabel(self.lyricGroup)
        font = QtGui.QFont()
        font.setPointSize(10)
        self.startLabel.setFont(font)
        self.startLabel.setObjectName("startLabel")
        self.startLabel.setText("Inicio:")
        self.gridLayout.addWidget(self.startLabel, 0, 0, 1, 1)
        font = QtGui.QFont()
        font.setPointSize(12)
        self.lyricsText = QtWidgets.QPlainTextEdit(self.lyricGroup)
        self.lyricsText.setObjectName("lyricsText")
        self.lyricsText.setFont(font)
        self.lyricsText.setPlainText(lyrics)
        self.gridLayout.addWidget(self.lyricsText, 0, 2, 2, 1)
        self.endLabel = QtWidgets.QLabel(self.lyricGroup)
        font = QtGui.QFont()
        font.setPointSize(10)
        self.endLabel.setFont(font)
        self.endLabel.setObjectName("endLabel")
        self.endLabel.setText("Final:")
        self.gridLayout.addWidget(self.endLabel, 1, 0, 1, 1)

        self.endTime = QtWidgets.QTimeEdit(self.lyricGroup)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum,
                                           QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.endTime.sizePolicy().hasHeightForWidth())
        self.endTime.setSizePolicy(sizePolicy)
        self.endTime.setMinimumSize(QtCore.QSize(100, 30))
        self.endTime.setMaximumSize(QtCore.QSize(75, 16777215))
        font = QtGui.QFont()
        font.setPointSize(11)
        self.endTime.setFont(font)
        self.endTime.setObjectName("endTime")
        self.endTime.setDisplayFormat("mm:ss:zzz")
        self.gridLayout.addWidget(self.endTime, 1, 1, 1, 1)
        self.endTime.setCurrentSectionIndex(0)
        self.endTime.setTime(end)

        self.startTime = QtWidgets.QTimeEdit(self.lyricGroup)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum,
                                           QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.startTime.sizePolicy().hasHeightForWidth())
        self.startTime.setSizePolicy(sizePolicy)
        self.startTime.setMinimumSize(QtCore.QSize(100, 30))
        self.startTime.setMaximumSize(QtCore.QSize(75, 16777215))
        font = QtGui.QFont()
        font.setPointSize(11)
        self.startTime.setFont(font)
        self.startTime.setCurrentSection(QtWidgets.QDateTimeEdit.MSecSection)
        self.startTime.setCurrentSection(2)
        self.startTime.setCurrentSectionIndex(2)
        self.startTime.setTime(start)
        self.startTime.setObjectName("startTime")
        self.startTime.setDisplayFormat("mm:ss:zzz")
        #self.startTime.timeChanged.connect(lambda:self.IncreaseTime(self.endTime))

        self.gridLayout.addWidget(self.startTime, 0, 1, 1, 1)
        self.verticalLayout.addWidget(self.lyricGroup, 0, QtCore.Qt.AlignTop)

        # add to list
        self.lyricList.append(self.lyricGroup)
        self.lyricCount += 1
        self.progressBar.setProperty("value", 0)

    def IncreaseTime(self, endTime):
        if endTime.time().currentTime() <= self.startTime.time().currentTime():
            startTimeObject = self.startTime.time()
            newTime = QtCore.QTime(0, startTimeObject.minute(),
                                   startTimeObject.second(),
                                   startTimeObject.msec())
            endTime.setTime(newTime)

    def CreateSRT(self):
        # For progress bar
        self.progressCount = 1

        itemSelected = self.textAppendList.currentText()

        # Check to see if file has been open
        if self.videoPath:

            self.newVideoPath = self.videoPath.split(
                ".")[0] + " - NoTranslation" + ".srt"
            srtFile = open(self.newVideoPath, "w", encoding="utf-8")

            if self.translateEnglish.isChecked(
            ) and self.translateSpanish.isChecked():
                self.newVideoPath = self.videoPath.split(".")[0] + ".srt"
                srtFile = open(self.newVideoPath, "w", encoding="utf-8")
            elif self.translateEnglish.isChecked():
                self.newVideoPath = self.videoPath.split(
                    ".")[0] + " - English" + ".srt"
                srtFile = open(self.newVideoPath, "w", encoding="utf-8")
            elif self.translateSpanish.isChecked():
                self.newVideoPath = self.videoPath.split(
                    ".")[0] + " - Spanish" + ".srt"
                srtFile = open(self.newVideoPath, "w", encoding="utf-8")

            for i in range(len(self.lyricList)):
                # lyrics
                childLyricsText = self.lyricList[i].findChild(
                    QtWidgets.QPlainTextEdit, "lyricsText")

                # start Time
                ## Format: hh:mm:ss:zzz
                childStartTime = self.lyricList[i].findChild(
                    QtWidgets.QTimeEdit, "startTime").time()

                # end Time
                childEndTime = self.lyricList[i].findChild(
                    QtWidgets.QTimeEdit, "endTime").time()

                #print("Start time : " + str(childStartTime.minute()) + str(childStartTime.second()) + str(
                #    childStartTime.msec()))

                # Number of iteration
                srtFile.write(str(self.progressCount) + "\n")

                # start time
                minuteTime = self.TwoCharSyntax(str(childStartTime.minute()))
                secondTime = self.TwoCharSyntax(str(childStartTime.second()))
                mSecTime = self.ThreeCharSyntax(str(childStartTime.msec()))
                srtFile.write("00:" + minuteTime + ":" + secondTime + "," +
                              mSecTime + " --> ")

                # end time
                minuteTime = self.TwoCharSyntax(str(childEndTime.minute()))
                secondTime = self.TwoCharSyntax(str(childEndTime.second()))
                mSecTime = self.ThreeCharSyntax(str(childEndTime.msec()))
                srtFile.write("00:" + minuteTime + ":" + secondTime + "," +
                              mSecTime)

                # Lyrics

                if self.translateEnglish.isChecked(
                ) and self.translateSpanish.isChecked():
                    result = self.translator.translate(
                        childLyricsText.toPlainText(), lang_tgt='en')
                    srtFile.write("\n" + result.rstrip().replace("\n", " ") +
                                  "\n")
                    result = self.translator.translate(
                        childLyricsText.toPlainText(), lang_tgt='es')
                    srtFile.write(itemSelected +
                                  result.rstrip().replace("\n", " ") +
                                  itemSelected + "\n\n")
                elif self.translateEnglish.isChecked():
                    result = self.translator.translate(
                        childLyricsText.toPlainText(), lang_tgt='en')
                    srtFile.write("\n" + result.rstrip().replace("\n", " ") +
                                  "\n\n")
                elif self.translateSpanish.isChecked():
                    result = self.translator.translate(
                        childLyricsText.toPlainText(), lang_tgt='es')
                    srtFile.write("\n" + itemSelected +
                                  result.rstrip().replace("\n", " ") +
                                  itemSelected + "\n\n")
                else:
                    srtFile.write(
                        "\n" +
                        childLyricsText.toPlainText().replace("\n", " ") +
                        "\n\n")
                progress = self.progressCount / self.lyricCount
                print(int(progress * 100))
                self.progressBar.setProperty("value", int(progress * 100))
                self.progressCount += 1

                if progress == 1:
                    print(self.newVideoPath)
                    openPath = self.newVideoPath.replace('/', '\\')
                    subprocess.Popen(r'explorer /select,"' + openPath + '"')

        else:
            self.ShowPopUpMessage()

    # check if string is only 1 character
    def TwoCharSyntax(self, str):
        if len(str) != 2:
            return "0" + str
        else:
            return str

    #Make into 3 char
    def ThreeCharSyntax(self, str):
        if len(str) == 1:
            return "00" + str
        elif len(str) == 2:
            return "0" + str
        elif len(str) >= 4:
            return str[-3:]
        else:
            return str

    def SetCurrentLyrics(self, currentTime):

        showingLyrics = False
        for i in range(len(self.lyricList)):
            # start Time
            ## Format: hh:mm:ss:zzz
            childStartTime = self.lyricList[i].findChild(
                QtWidgets.QTimeEdit, "startTime").time()

            # convert to msecs
            minuteTime = childStartTime.minute()
            secondTime = childStartTime.second()
            mSecTime = childStartTime.msec()
            startTotal = (minuteTime * 60000) + (secondTime * 1000) + mSecTime

            # end Time
            childEndTime = self.lyricList[i].findChild(QtWidgets.QTimeEdit,
                                                       "endTime").time()

            # convert to msecs
            minuteTime = childEndTime.minute()
            secondTime = childEndTime.second()
            mSecTime = childEndTime.msec()
            endTotal = (minuteTime * 60000) + (secondTime * 1000) + mSecTime

            if currentTime >= startTotal and currentTime <= endTotal:
                childLyricsText = self.lyricList[i].findChild(
                    QtWidgets.QPlainTextEdit, "lyricsText")
                self.currentLyrics.setText(childLyricsText.toPlainText())
                showingLyrics = True
        if showingLyrics == False:
            self.currentLyrics.setText("")

    def ShowPopUpMessage(self):
        errorMsg = QMessageBox()
        errorMsg.setWindowTitle("Error")
        errorMsg.setText("Por favor selecciona un video primero")
        x = errorMsg.exec_()
Exemple #15
0
class jaabaGUI(QMainWindow):
    """ controller for the blob labeling GUI"""

    def __init__(self,parent=None):
        self.debugMode = True
        self.debugVideoPath = '/Users/071cht/Desktop/Lab/jaabagui/testt.mjpeg.avi'

        QMainWindow.__init__(self,parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.installEventFilter(self)
        self.setFocusPolicy(Qt.StrongFocus)
        #add new slider
        # self.positionSlider=QSlider(Qt.Horizontal)
        # self.positionSlider.setGeometry (800,800,100,30)
        # self.positionSlider.setRange(0, 0)
        # self.positionSlider.sliderMoved.connect(self.setPosition)

        #setup Video
        #video player
        self.mediaPlayer1 = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.mediaPlayer2 = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.mediaPlayer2.setNotifyInterval(10)
        #self.mediaPlayer.metaDataChanged.connect(self.metaDataChanged)
        self.mediaPlayer1.durationChanged.connect(self.durationChanged)
        self.mediaPlayer1.positionChanged.connect(self.positionChanged)
        self.mediaPlayer2.positionChanged.connect(self.positionChanged)
        #self.mediaPlayer2.positionChanged.connect(self.paintEvent)
        

        #visualizetion
        self.scene = QGraphicsScene()
        self.ui.graphicsView.setScene(self.scene)
        #self.scene.setBackgroundBrush(Qt.black)
        self.videoItem1 = QGraphicsVideoItem()
        self.videoItem2 = Video()
        self.scene.addItem(self.videoItem1)
        self.scene.addItem(self.videoItem2)
        self.mediaPlayer1.setVideoOutput(self.videoItem1)
        self.mediaPlayer2.setVideoOutput(self.videoItem2)

       

        #slider bar
        self.ui.horizontalSlider.setRange(0, 0)
        self.ui.horizontalSlider.sliderMoved.connect(self.setPosition)
        # self.ui.horizontalSlider.sliderPressed.connect(self.sliderPressed)

        #draw on video
        self.flyCanvas= TargetView()
        self.scene.addItem(self.flyCanvas)
        #give reference to target view
        self.flyCanvas.setWindowReference(self)

        #lineEdit signals:
        self.ui.lineEdit.returnPressed.connect(self.lineEditChanged)



        #callbacks
        self.ui.actionQuit.triggered.connect(self.quit)
        self.ui.actionLoad_Project.triggered.connect(self.loadVideo)
        self.ui.actionImport_Labels.triggered.connect(self.loadLabels)
        #self.ui.buttonPlay.clicked[bool].connect(self.setToggleText)
        self.ui.buttonPlay.clicked.connect(self.play)
        self.ui.actionSave.triggered.connect(self.saveLabels)
        ## print self.ui.graphicsView.sizeHint()

        #behavior Button
        self.ui.buttonBehavior.clicked.connect(self.behaviorButtonClick)
        self.ui.buttonNone.clicked.connect(self.noneButtonClick)

        #initialization
        self.loaded = False
        self.videoFilename = None
        self.frame_count=None
        self.width=None
        self.height=None
        self.frame_trans=None
        self.previous_frame=0
        self.current_frame=0
        self.behaviorButtonStart = False
        self.noneButtonStart = False
        self.currentFly=1

        #initialize flyInfo
        #self.setCurrentFly(self.currentFly)

        # register flyid changed callback
        self.flyCanvas.onCurrentFlyIdChanged(self.currentFlyIdChangedCallback)
        self.flyCanvas.setCurrentFlyId(self.currentFly)

        # when double click on video, change fly id in target view
        self.videoItem2.onDoubleClick(self.flyCanvas.setCurrentFlyIdByXY)

        ########################
        # DEBUG PART HERE!!!!! #
        ########################
        if (self.debugMode):
            self.debugLoadVideo()

    # add label UI related when load video   
    def showEvent(self, evt):
        super(jaabaGUI, self).showEvent(evt)
        ##### HERE THE WINDOW IS LOADED!!!!!!!!
        # self.loadLabelUI()

    def loadLabelUI(self):
         #labels
        self.labelScene = QGraphicsScene()

        self.ui.graphLabels.setScene(self.labelScene)
        # the size is only accurate after the window fully displayed
        labelUIWidth = self.ui.graphLabels.width()
        labelUIHeight = self.ui.graphLabels.height()-1

        self.labelScene.setSceneRect(0,0,labelUIWidth,labelUIHeight)

        
        self.labelUI = LabelUI()
        # visiableWidth = 850
        # height = 30
        # visiableFrameNum = 850

        self.labelUI.setWidthPerFrame(850.0/850.0)
        # print '850/500',850.0/850.0b
        # print 'length_perframe is ', self.labelUI.widthPerFrame 
        # 850 is the original length of graphLabel
        total_length= self.labelUI.widthPerFrame * self.frame_count
        self.labelUI.setVisiableSize(total_length,30)

        # set start position
        self.labelUI.setPos(labelUIWidth/2,0)


        print 'frame_count is ', self.frame_count
        print 'total length is', total_length
        
        self.labelScene.addItem(self.labelUI)

        # middle line ui
        self.labelUIMiddleLine = LabelUIMiddleLine()
        self.labelScene.addItem(self.labelUIMiddleLine)
        self.labelUIMiddleLine.setPos(labelUIWidth/2,0)
       


        # self.labelUI.setPos(QPointF(-100,0))
        self.writeLog('Label UI loaded')

    def eventFilter(self, obj, event):
  
    	if (event.type() == PyQt5.QtCore.QEvent.KeyPress):
    		# http://qt-project.org/doc/qt-4.8/qt.html#Key-enum
    		key = event.key()
    		
    		if (key == Qt.Key_Up) :
    			curr_frame= int(float(self.ui.lineEdit.text()))
    			curr_frame= curr_frame-30
    			media_position= int(round(curr_frame*self.frame_trans))

    			# print curr_frame, media_position
    			self.mediaPlayer1.setPosition(media_position) 
    			self.mediaPlayer2.setPosition(media_position)

    			# print 'down -30'
    		elif (key == Qt.Key_Right):
    			curr_frame= int(float(self.ui.lineEdit.text()))
    			# print 'right +1'
    			# print curr_frame
    			curr_frame= curr_frame+1
    			media_position= int(round(curr_frame*self.frame_trans))
    			# print 'curr_frame',curr_frame
    			# print 'frame_trans',self.frame_trans
    			# print ' curr_frame*self.frame_trans',curr_frame*self.frame_trans
    			# print 'media_position',media_position

    			# print curr_frame, media_position
    			self.mediaPlayer1.setPosition(media_position) 
    			self.mediaPlayer2.setPosition(media_position)
    			# self.mediaPlayerPositionChanged(media_position)
    		elif (key == Qt.Key_Left):
    			curr_frame= int(float(self.ui.lineEdit.text()))
    			curr_frame= curr_frame-1
    			media_position= int(round(curr_frame*self.frame_trans))
    			self.mediaPlayer1.setPosition(media_position) 
    			self.mediaPlayer2.setPosition(media_position)
    			# print 'left -1'
    		elif (key == Qt.Key_Down):
    			curr_frame= int(float(self.ui.lineEdit.text()))
    			curr_frame= curr_frame+30
    			media_position= int(round(curr_frame*self.frame_trans))
    			self.mediaPlayer1.setPosition(media_position) 
    			self.mediaPlayer2.setPosition(media_position)
    			# print 'up +30'
    		return True

    		

    	return False


    # ###actions starts from here###
    def quit(self):
        QApplication.quit()

    def loadVideo(self):
        
        # print QMediaPlayer.supportedMimeTypes()

        self.writeLog("Loading video...")

        self.videoFilename = QFileDialog.getOpenFileName(self, 'Open File', '.')[0]
        if not self.videoFilename:
            self.writeLog("User cancelled - no video loaded")
            return
        else:
       		cap=cv2.VideoCapture(self.videoFilename)
	    	self.frame_count=cap.get(cv2.CAP_PROP_FRAME_COUNT)
	    	self.width=cap.get(3)
	    	self.height=cap.get(4)

	        self.mediaPlayer2.setMedia(QMediaContent(QUrl.fromLocalFile(self.videoFilename )))
	        self.mediaPlayer1.setMedia(QMediaContent(QUrl.fromLocalFile(self.videoFilename )))
	        self.ui.buttonPlay.setEnabled(True)
            # self.mediaPlayer2.setVideoOutput(self.videoItem2)
            # self.mediaPlayer1.setVideoOutput(self.videoItem1)
            # size= self.videoItem2.nativeSize()
            # # print size
            ## print self.mediaPlayer.duration()
          
            ## print self.mediaPlayer.metaData()
        self.writeLog("Video loaded!")

        # init label related ui
        self.loadLabelUI()


    def debugLoadVideo(self):

        self.videoFilename = self.debugVideoPath

        cap=cv2.VideoCapture(self.videoFilename)
        self.frame_count=cap.get(cv2.CAP_PROP_FRAME_COUNT)
        self.width=cap.get(3)
        self.height=cap.get(4)

        self.mediaPlayer2.setMedia(QMediaContent(QUrl.fromLocalFile(self.videoFilename )))
        self.mediaPlayer1.setMedia(QMediaContent(QUrl.fromLocalFile(self.videoFilename )))
        self.ui.buttonPlay.setEnabled(True)
        self.writeLog("Video loaded!")

        QTimer.singleShot(1000, self.loadLabelUI)

    def play(self):
    	
        self.videoItem1.setAspectRatioMode(0)
        self.videoItem2.setAspectRatioMode(0)
        self.scene.setSceneRect(0,0,self.ui.graphicsView.width(),self.ui.graphicsView.height())
        self.videoItem1.setSize(QSizeF(self.ui.graphicsView.width()/2,self.ui.graphicsView.height()))
        self.videoItem2.setSize(QSizeF(self.ui.graphicsView.width()/2,self.ui.graphicsView.height()))
        self.videoItem1.setPos(QPointF(0,0))
        self.videoItem2.setPos(QPointF(self.ui.graphicsView.width()/2,0))
        self.flyCanvas.setPos(QPointF(self.ui.graphicsView.width()/2,0))

        # custom function setXYScale
        self.videoItem2.setXYScale(self.width,self.height,self.ui.graphicsView.width()/2,self.ui.graphicsView.height())
        self.flyCanvas.setXYScale(self.width,self.height,self.ui.graphicsView.width()/2,self.ui.graphicsView.height())




        if self.mediaPlayer1.state() == QMediaPlayer.PlayingState:
        	self.ui.buttonPlay.setIcon(self.ui.style().standardIcon(PyQt5.QtWidgets.QStyle.SP_MediaPlay))
        	self.ui.buttonPlay.setText("Play")
        	self.mediaPlayer1.pause()
        	self.writeLog("Video paused")
        else: 
        	self.ui.buttonPlay.setIcon(self.ui.style().standardIcon(PyQt5.QtWidgets.QStyle.SP_MediaPause))
	        self.ui.buttonPlay.setText("Stop")
	        
	        self.mediaPlayer1.play()
	        self.writeLog("Playing video")

        if self.mediaPlayer2.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer2.pause()
        else: 
            self.mediaPlayer2.play()

    def loadLabels(self):

        self.writeLog("Loading labels from file...")
        self.labelFilename = QFileDialog.getOpenFileName(self, 'Open File', '.')[0]
        self.labelUI.labelData = pickle.load(open(self.labelFilename,"rb"))
        self.writeLog("Label loaded from file:" + self.labelFilename)

    def saveLabels(self):
        # Now it can only save to current file. Will add an poput window to choose path later
        pickle.dump( self.labelUI.labelData, open( "newLabels.p", "wb" ) )
  

    def setPosition(self, position):
    	self.mediaPlayer1.setPosition(position) 
    	self.mediaPlayer2.setPosition(position) 

    # when position of media changed, set slider and text box accordingly.
    def positionChanged(self, position):
        #test change labelui position
        # self.labelUI.startLabel();
        # self.labelUI.update()
        previous_frame=  self.previous_frame
        curr_frame= int(round(position/self.frame_trans))
        self.current_frame=curr_frame
        frame_change= previous_frame-curr_frame
        move_width= frame_change * self.labelUI.widthPerFrame
        self.previous_frame= curr_frame

        self.labelUI.moveBy(move_width,0)

        self.labelUI.setCurrentFrame(curr_frame)
        # enforce labelUI paint once
        self.labelUI.update()
       
        # self.labelUI.setPos(self.labelUI.mapToParent(1,0));
        # self.labelUI.update()

    	# # print 'triggered position'
    	# # print position
    	# # print 'cur position'
    	# # print self.mediaPlayer2.position()
    	self.updateLineEdit(position)
    	self.updateSliderAndGraph(position)
    	
       #  self.ui.horizontalSlider.setValue(position)

       #  if isinstance(self.frame_trans,float):
	      #   # # print type(position),position
	      #   # # print type(self.frame_trans),self.frame_trans 
	      #   # # print position/self.frame_trans
	     	# self.ui.lineEdit.setText(str(int(round(position/self.frame_trans))))
	     	# self.flyCanvas.getFrame(int(round(position/self.frame_trans)))
	     	# self.flyCanvas.isManualCalled = True;
	     	# self.flyCanvas.update()

       #  self.writeLog(str(position))    
       # # self.updateMediaControlUI(position)
       # # self.flyCanvas.update()

    def updateSliderAndGraph(self, position):
    	self.ui.horizontalSlider.setValue(position)
    	if isinstance(self.frame_trans,float):
    		self.flyCanvas.getFrame(int(round(position/self.frame_trans)))
    		self.flyCanvas.isManualCalled = True
    		self.flyCanvas.update()

        #self.writeLog(str(position)) 
    def updateLineEdit(self, position): 
        # # print self.width
        # # print self.height
    	if isinstance(self.frame_trans,float):
	        # # print type(position),position
	        # # print type(self.frame_trans),self.frame_trans 
	        # # print position/self.frame_trans
	     	self.ui.lineEdit.setText(str(int(round(position/self.frame_trans))))

    def durationChanged(self, duration):
	    self.ui.horizontalSlider.setRange(0, duration) 
	    self.frame_trans=self.mediaPlayer1.duration()/self.frame_count
	    ## print self.frame_trans

	#def eventFilter(self,source,event):
		#if (event.type()==PyQt5.QtCore.QEvent.MousePress and source is self.videoItem2):
		# 	pos=event.pos()
		# 	# print('mouse position: (%d,%d)' % (pos.x(),pos.y()))
	 #    return PyQt5.QtGui.QWidget.eventFilter(self, source, event)

    def writeLog(self,text):
        self.ui.log.setText(text)

    # def eventFilter (self.ui.lineEdit,event):
    #     if event.type()==PyQt5.QtCore.QEvent

    def lineEditChanged(self):
    	#set position of media
    	curr_frame= int(float(self.ui.lineEdit.text()))
    	media_position= int(round(curr_frame*self.frame_trans))
    	self.mediaPlayer1.setPosition(media_position) 
    	self.mediaPlayer2.setPosition(media_position)
    	# print 'setPosition'
    	# print media_position
    	# print 'after set'
    	# print self.mediaPlayer2.position()
    	# self.updateSliderAndGraph(media_position)


    def behaviorButtonClick(self):
        # flip flag
        self.behaviorButtonStart = not self.behaviorButtonStart

        # check click to start or stop
        if (self.behaviorButtonStart):
            # start labeling
            self.labelUI.startLabel(self.ui.comboBox.currentIndex(),'',self.current_frame)
            self.writeLog('start labeling')


        else:
            # stop lableing
            self.labelUI.stopLabel()
            self.writeLog('stop labeling')

    def noneButtonClick(self):
           # flip flag
        self.noneButtonStart = not self.noneButtonStart

        # check click to start or stop
        if (self.noneButtonStart):
            # start labeling
            self.labelUI.startLabel(self.ui.comboBox.currentIndex(),'_none',self.current_frame)
            self.writeLog('start labeling')
        else:
            # stop lableing
            self.labelUI.stopLabel()
            self.writeLog('stop labeling')


    # set CurrentFly when fly changed! 
    def setCurrentFly(self,fly):
        self.currentFly = fly
        self.ui.flyInfo.setPlainText('FlyID:' + str(self.currentFly))
        self.flyCanvas.currentFly=fly

    def currentFlyIdChangedCallback(self,fly):
        print 'callback!!!!!';
        self.currentFly = fly
        self.ui.flyInfo.setPlainText('FlyID:' + str(self.currentFly))
Exemple #16
0
class ReplayWindow(QWidget):
    def __init__(self, stylesheet, **kwargs):
        super().__init__(**kwargs)

        self.setStyleSheet(stylesheet)

        self.offset_defined = None
        self.time_result = None

        self.layout = QVBoxLayout()
        self.timer_label = self.make_label()
        self.video_w = QVideoWidget()

        self.v_player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.v_player.setVideoOutput(self.video_w)
        self.v_player.setNotifyInterval(7)
        self.v_player.positionChanged.connect(self.update_time_label)
        self.v_player.stateChanged.connect(self.video_ended)

        self.music_player = QMediaPlayer(None, QMediaPlayer.LowLatency)
        self.recording_player = QMediaPlayer(None, QMediaPlayer.LowLatency)

        self.controls = ReplayWindowControls(self.v_player,
                                             self.recording_player,
                                             self.music_player)

        self.layout.addWidget(self.timer_label)
        self.layout.addWidget(self.video_w)
        self.layout.addWidget(self.controls)
        self.layout.setSpacing(0)
        self.layout.setContentsMargins(0, 0, 0, 0)

        self.setLayout(self.layout)

    @staticmethod
    def format_millis(millis: int) -> str:
        if millis >= 0:
            minutes = int(millis / (60 * 1000))
            seconds = int((millis % (60 * 1000)) / 1000)
            ms = millis % 1000
            return f"{minutes:02}:{seconds:02}:{ms:03}"
        else:
            millis = -millis
            minutes = int(millis / (60 * 1000))
            seconds = int((millis % (60 * 1000)) / 1000)
            ms = millis % 1000
            return f"-{minutes:02}:{seconds:02}:{ms:03}"

    def update_time_label(self):
        if self.time_result is None:
            return

        position = self.v_player.position()
        if position > self.time_result:
            text = self.format_millis(self.time_result - self.offset_defined)
        else:
            text = self.format_millis(position - self.offset_defined)

        self.timer_label.setText(text)

    def make_label(self):
        timer_label = QLabel()
        timer_label.setText(self.format_millis(0))
        timer_label.setAlignment(QtCore.Qt.AlignRight)
        timer_label.setStyleSheet("""
			color: #CCCCCC;
			font-size:32px;
			padding: 16px;
			position: absolute;			
			""")
        return timer_label

    def replay(self, file: str):
        self.music_player.stop()
        self.v_player.stop()
        self.recording_player.stop()

        v_path = QFileInfo(f"recordings/{file}2.avi").absoluteFilePath()
        a_path = QFileInfo(f"recordings/{file}.wav").absoluteFilePath()
        self.v_player.setMedia(QMediaContent(QUrl.fromLocalFile(v_path)))
        self.recording_player.setMedia(
            QMediaContent(QUrl.fromLocalFile(a_path)))

        self.music_player.play()
        time.sleep(recording_latency / 1000)  # Latency guessed empirically
        self.v_player.play()
        self.recording_player.play()

    def end_replay(self):
        self.music_player.stop()
        self.v_player.stop()
        self.recording_player.stop()

    def video_ended(self):
        if self.v_player.state() == QMediaPlayer.StoppedState:
            self.music_player.stop()
            self.v_player.setPosition(self.v_player.duration() - 10)
            self.v_player.play()
            self.v_player.pause()
Exemple #17
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.my_state = _STATE_PLAY

        # Controles principales para organizar la ventana.
        self.widget = QWidget(self)
        self.layout = QVBoxLayout()
        self.bottom_layout = QHBoxLayout()

        # Control de reproducción de video de Qt.
        self.video_widget = QVideoWidget(self)
        self.media_player = QMediaPlayer()
        self.media_player.setMedia(
            QMediaContent(QUrl.fromLocalFile(MY_PATH + VIDEO_PATH)))
        self.media_player.setVideoOutput(self.video_widget)

        # Botones de reproducción y pausa.
        self.play_button = QPushButton("Pausa", self)
        self.stop_button = QPushButton("Detener", self)

        # Deslizadores para el volumen y transición del video.
        self.seek_slider = QSlider(Qt.Horizontal)
        self.volume_slider = QSlider(Qt.Horizontal)
        self.volume_slider.setRange(0, 100)
        self.volume_slider.setValue(self.media_player.volume())

        #self.volume_slider.sliderMoved.connect(self.media_player.setVolume)

        # actualizar la posicion

        #self.seek_slider.sliderMoved.connect(self.change_media_player)

        #self.media_player.durationChanged.connect(self.change_duration)

        # Acomodar controles en la pantalla.
        self.layout.addWidget(self.video_widget)
        self.layout.addLayout(self.bottom_layout)

        self.layout.addWidget(self.seek_slider)

        self.bottom_layout.addWidget(self.play_button)
        self.bottom_layout.addWidget(self.stop_button)
        self.bottom_layout.addWidget(self.volume_slider)

        # Conectar los eventos con sus correspondientes funciones.
        self.play_button.clicked.connect(self.play_clicked)
        #self.stop_button.clicked.connect(self.stop_clicked)
        self.media_player.stateChanged.connect(self.state_changed)

        # Personalizar la ventana.
        self.setWindowTitle("Reproductor de video")
        self.resize(800, 600)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.bottom_layout.setContentsMargins(0, 0, 0, 0)
        self.widget.setLayout(self.layout)
        self.setCentralWidget(self.widget)

        self.media_player.positionChanged.connect(self.pos_changed)

        # Reproducir el video.
        self.my_signal = mi_senal()
        self.my_signal.sig.connect(self.saySomeWords)

        self.timer = QTimer()
        self.timer.timeout.connect(self.tick)
        self.timer.start(500)
        self.parar = False

        self.media_player.setNotifyInterval(1000)
        self.media_player.play()

    def tick(self):
        #print ("este es mi timer")
        if self.parar == True:
            self.media_player.pause()
            self.timer.stop()

    # senales relacionadas a eventos
    @pyqtSlot(str)
    def saySomeWords(self, words):
        print(words)
        self.media_player.pause()

    def hand_my_signal(self):
        print("Esta es mi senal")

    def play_clicked(self):
        """
        Comenzar o resumir la reproducción.
        """
        self.my_signal.sig.emit("hola a todo mundo")
        #if (self.media_player.state() in
        #    (QMediaPlayer.PausedState, QMediaPlayer.StoppedState)):
        #    self.media_player.play()
        #else:
        #    self.media_player.pause()

    def pos_changed(self, value):
        print("la posicion es:%d" % (value))
        if value >= 4000:
            self.parar = True
        #    self.media_player.positionChanged.disconnect(self.pos_changed)

        #self.media_player.pause()

    def state_changed(self, newstate):
        print("cambio de estado")
Exemple #18
0
class WorkSpace(QMainWindow):
    def __init__(self):
        super().__init__()
        self.widget = QWidget(self)
        self.positionVideo = 0

        self.createVideo()
        self.createPlayButton()
        self.createPositionSlider()
        self.createTimelineSlider()
        self.createLayout()

    def createVideo(self):
        self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)

        self.mediaPlayer.setNotifyInterval(10)
        self.videoWidget = QVideoWidget()

        self.mediaPlayer.setVideoOutput(self.videoWidget)
        self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
        self.mediaPlayer.positionChanged.connect(self.positionChanged)
        self.mediaPlayer.durationChanged.connect(self.durationChanged)

    def createPlayButton(self):
        self.playButton = QPushButton()
        self.playButton.setEnabled(False)
        self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
        self.playButton.clicked.connect(self.play)

    def createPositionSlider(self):
        self.positionSlider = QSlider(Qt.Horizontal, self.widget)
        self.positionSlider.sliderMoved.connect(self.setPosition)

    def createTimelineSlider(self):
        self.timelineSlider = QSlider(Qt.Horizontal, self.widget)
        self.timelineSlider.setTickInterval(100)
        self.timelineSlider.setTickPosition(QSlider.TicksBelow)
        self.timelineSlider.sliderMoved.connect(self.setPosition)

    def createLayout(self):
        self.timelineWidget = QGraphicsView()
        screenSize = QDesktopWidget().availableGeometry()
        self.timelineWidget.setFixedSize(screenSize.width() - 20, screenSize.height() // 8)

        historyBox = QVBoxLayout()
        text = QLabel('<font size=5> History </font>')
        self.undoView = QUndoView()
        historyBox.addWidget(text)
        historyBox.addWidget(self.undoView)
        history = QWidget()
        history.setLayout(historyBox)

        sliderBox = QHBoxLayout()
        sliderBox.addWidget(self.playButton)
        sliderBox.addWidget(self.positionSlider)

        videoBox = QVBoxLayout()
        videoBox.addWidget(self.videoWidget)
        videoBox.addLayout(sliderBox)
        video = QWidget()
        video.setLayout(videoBox)

        splitter = QSplitter(Qt.Horizontal)
        splitter.setStyleSheet("QSplitter::handle { background-color: #d3d3d3 }")
        splitter.addWidget(history)
        splitter.addWidget(video)

        vbox = QVBoxLayout()
        vbox.addWidget(splitter)
        vbox.addWidget(self.timelineSlider)
        vbox.addWidget(self.timelineWidget)

        self.widget.setLayout(vbox)

    def play(self):
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()
        else:
            self.mediaPlayer.play()

    def mediaStateChanged(self):
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
        else:
            self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))

    def positionChanged(self, position):
        self.positionVideo = position
        self.positionSlider.setValue(position)
        self.timelineSlider.setValue(position)

    def durationChanged(self, duration):
        self.durationVideo = duration
        self.positionSlider.setRange(0, duration)
        self.timelineSlider.setRange(0, duration)

        self.timelineLogic = TimelineLogic(duration)
        self.timelineWidget.setScene(self.timelineLogic.scene)
        self.undoView.setStack(self.timelineLogic.undoStack)

    def setPosition(self, position):
        self.mediaPlayer.setPosition(position)
Exemple #19
0
class VideoWindow(QWidget):
    def __init__(self, vidPath):
        super().__init__()
        self.fullPath = vidPath
        self.startTime = 0
        self.endTime = 0
        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()
        self.setLayout(layout)
        self.setWindowTitle(self.fullPath)

        self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)

        self.videoWidget = QVideoWidget()

        self.playButton = QPushButton()
        self.playButton.setEnabled(True)
        self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
        self.playButton.setFixedWidth(100)
        self.playButton.setFixedHeight(50)
        self.playButton.clicked.connect(self.play)

        self.trimButton = QPushButton("Trim")
        self.trimButton.setFixedWidth(150)
        self.trimButton.setFixedHeight(50)
        self.trimButton.clicked.connect(self.trimVid)

        self.positionSlider = QSlider(QtCore.Qt.Horizontal)
        self.positionSlider.setRange(0, 0)
        self.positionSlider.sliderMoved.connect(self.setPosition)

        self.rangeSlider = qrangeslider.QRangeSlider()
        self.rangeSlider.setRange(0, 0)
        self.rangeSlider.endValueChanged.connect(self.adjustForEnd)
        self.rangeSlider.startValueChanged.connect(self.adjustForStart)
        self.rangeSlider.setFixedHeight(15)

        self.startTimeInput = QTimeEdit()
        self.endTimeInput = QTimeEdit()
        self.startTimeInput.setDisplayFormat('hh:mm:ss.zzz')
        self.endTimeInput.setDisplayFormat('hh:mm:ss.zzz')

        self.startTimeInput.timeChanged.connect(self.startInputChanged)
        self.endTimeInput.timeChanged.connect(self.endInputChanged)

        self.mediaPlayer.setMedia(
            QMediaContent(QtCore.QUrl.fromLocalFile(self.fullPath)))

        layout.addWidget(self.videoWidget)
        self.mediaPlayer.setVideoOutput(self.videoWidget)
        self.mediaPlayer.setNotifyInterval(10)
        self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
        self.mediaPlayer.positionChanged.connect(self.positionChanged)
        self.mediaPlayer.durationChanged.connect(self.durationChanged)

        controlLayout = QVBoxLayout()
        controlLayout.setContentsMargins(0, 0, 0, 0)
        controlLayout.addWidget(self.rangeSlider)
        controlLayout.addWidget(self.positionSlider)

        timeInputLayout = QHBoxLayout()
        timeInputLayout.addWidget(self.playButton)
        timeInputLayout.addWidget(self.startTimeInput)
        timeInputLayout.addWidget(self.endTimeInput)
        timeInputLayout.addWidget(self.trimButton)

        controlLayout.addLayout(timeInputLayout)

        layout.addLayout(controlLayout)

        self.mediaPlayer.play()

        self.resize(1024, 700)

        self.show()

    def closeEvent(self, event):
        self.mediaPlayer.stop()
        self.videoWidget.setParent(None)
        self.mediaPlayer.setParent(None)
        self.mediaPlayer.deleteLater()
        self.videoWidget.deleteLater()

    def trimVid(self):
        self.trimButton.setEnabled(False)
        outName = mytools.getAvailableName(self.fullPath, 'Trim')
        print(outName)
        trimStartTime = self.startTimeInput.time().toString('hh:mm:ss.zzz')
        trimEndTime = self.endTimeInput.time().toString('hh:mm:ss.zzz')
        try:
            ff = FFmpeg(inputs={self.fullPath: None},
                        outputs={
                            outName: [
                                '-ss',
                                trimStartTime,
                                '-to',
                                trimEndTime,
                                '-c:v',
                                'copy',
                                '-c:a',
                                'copy',
                            ]
                        })
            ff.run()
        except Exception as e:
            msg = QMessageBox()
            msg.setWindowTitle("Trim Failed")
            msg.setText(str(e))
            msg.setIcon(QMessageBox.Critical)

            showMsg = msg.exec_()
        self.trimButton.setEnabled(True)

    def play(self):
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()
        else:
            self.mediaPlayer.play()

    def mediaStateChanged(self, state):
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.playButton.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPause))
        else:
            self.playButton.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPlay))

    def positionChanged(self, position):
        self.positionSlider.setValue(position)
        if position > self.endTime:
            self.mediaPlayer.setPosition(self.startTime)

    def adjustForStart(self, startPos):
        self.startTime = startPos
        self.mediaPlayer.setPosition(startPos)

        self.startTimeInput.setTime(QtCore.QTime(0, 0).addMSecs(startPos))
        self.endTimeInput.setMinimumTime(QtCore.QTime(0, 0).addMSecs(startPos))

    def adjustForEnd(self, endPos):
        self.endTime = endPos
        if self.positionSlider.value() > endPos:
            self.mediaPlayer.setPosition(endPos)

        self.endTimeInput.setTime(QtCore.QTime(0, 0).addMSecs(endPos))
        self.startTimeInput.setMaximumTime(QtCore.QTime(0, 0).addMSecs(endPos))

    def startInputChanged(self, inputTime):
        self.rangeSlider.setStart(QtCore.QTime(0, 0, 0, 0).msecsTo(inputTime))

    def endInputChanged(self, inputTime):
        self.rangeSlider.setEnd(QtCore.QTime(0, 0, 0, 0).msecsTo(inputTime))

    def durationChanged(self, duration):
        self.positionSlider.setRange(0, duration)
        self.rangeSlider.setMax(duration)
        self.rangeSlider.setEnd(duration)

        self.startTimeInput.setMinimumTime(QtCore.QTime(0, 0))
        self.endTimeInput.setMinimumTime(QtCore.QTime(0, 0))
        self.endTimeInput.setTime(QtCore.QTime(0, 0).addMSecs(duration))
        self.startTimeInput.setMaximumTime(
            QtCore.QTime(0, 0).addMSecs(duration))
        self.endTimeInput.setMaximumTime(QtCore.QTime(0, 0).addMSecs(duration))

    def setPosition(self, position):
        self.mediaPlayer.setPosition(position)
Exemple #20
0
class SongList(QWidget):
    def __init__(self, parent=None):
        super(SongList, self).__init__(parent)

        os.chdir(os.path.dirname(os.path.abspath(__file__)))
        resourcesPath = os.getcwd()
        resourcesPath = os.path.join(resourcesPath, "resources")

        self.PLAY_ICON = QIcon(QPixmap(os.path.join(resourcesPath, "play.png")))
        self.PAUSE_ICON = QIcon(QPixmap(os.path.join(resourcesPath, "pause.png")))
        self.STOP_ICON = QIcon(QPixmap(os.path.join(resourcesPath, "stop.png")))
        self.DELETE_ICON = QIcon(QPixmap(os.path.join(resourcesPath, "delete.png")))

        self.setupMediaPlayer()
        self.setupUi()

    def setupMediaPlayer(self):
        self.mediaPlayer = QMediaPlayer()

        self.mediaPlayer.setNotifyInterval(1)
        self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
        self.mediaPlayer.positionChanged.connect(self.positionChanged)
        self.mediaPlayer.durationChanged.connect(self.durationChanged)

    def setupUi(self):
        self.setWindowTitle("List of songs")
        mainLayout = QHBoxLayout(self)

        verticalListLayout = QVBoxLayout()
        self.songsListWidget = QListWidget()
        self.songsListWidget.setContextMenuPolicy(Qt.CustomContextMenu)
        self.songsListWidget.customContextMenuRequested.connect(self.listWidgetRightClick)
        verticalListLayout.addWidget(self.songsListWidget)

        miniHorizontalLayout = QHBoxLayout()
        locatorLine = QLineEdit()
        locatorLine.setPlaceholderText("Locator")
        locatorBox = QComboBox()
        items = ["Title", "Status", "Description", "Style", "All"]
        locatorBox.addItems(items)
        locatorBox.setCurrentIndex(len(items)-1)

        miniHorizontalLayout.addWidget(locatorLine)
        miniHorizontalLayout.addWidget(locatorBox)

        locatorLine.textChanged.connect(lambda:self.populateList(locatorLine.text(), locatorBox.currentText()))

        verticalListLayout.addLayout(miniHorizontalLayout)

        self.mainForm = QGroupBox()
        self.mainForm.setTitle("Details")

        mainLayout.addLayout(verticalListLayout)
        mainLayout.addWidget(self.mainForm)

        self.populateList()
        self.mainFormSetupUi()
        #self.show()

        self.songsListWidget.currentRowChanged.connect(self.changePage)
    
    def mainFormSetupUi(self):

        """title, status style, duration, descriptin, location, project,
        variation_another_song, timestamp"""

        mainLayout = QVBoxLayout(self.mainForm)

        #Horizontal Layout 1
        horizontalLayout1 = QHBoxLayout()

        titleLabel = QLabel("Song name:")
        self.titleEdit = QLineEdit()

        self.titleEdit.editingFinished.connect(self.checkSong)
        self.titleEdit.textChanged.connect(self.validateSong)

        horizontalLayout1.addWidget(titleLabel)
        horizontalLayout1.addWidget(self.titleEdit)


        #Horizontal Layout 2
        horizontalLayout2 = QHBoxLayout()
        statusLabel = QLabel("Status:")
        self.statusBox = QComboBox()

        dateLabel = QLabel("Date:")
        self.dateEdit = QDateTimeEdit()
        self.dateEdit.setCalendarPopup(True)

        horizontalLayout2.addWidget(statusLabel)
        horizontalLayout2.addWidget(self.statusBox)
        horizontalLayout2.addStretch(1)
        horizontalLayout2.addWidget(dateLabel)
        horizontalLayout2.addWidget(self.dateEdit)


        #Style Groupbox, widgets added automatically
        self.styleGroupBox = QGroupBox()
        self.styleGroupBox.setTitle("Style:")
        self.styleLayout = QGridLayout(self.styleGroupBox)

        horizontalLayout3 = QHBoxLayout()
        durationLabel = QLabel("Duration:")
        self.durationLine = QTimeEdit()
        self.durationLine.setDisplayFormat("mm:ss")

        projectLabel = QLabel("Project")

        self.projectComboBox = QComboBox()
        self.projectComboBox.setEditable(True)

        horizontalLayout3.addWidget(durationLabel)
        horizontalLayout3.addWidget(self.durationLine)
        horizontalLayout3.addWidget(projectLabel)
        horizontalLayout3.addWidget(self.projectComboBox)

        horizontalLayout4 = QHBoxLayout()
        descriptionLabel = QLabel("Description:")
        variationLabel = QLabel("Variation from another song: ")
        self.variationLine = QLineEdit()

        horizontalLayout4.addWidget(descriptionLabel)
        horizontalLayout4.addStretch(1)
        horizontalLayout4.addWidget(variationLabel)
        horizontalLayout4.addWidget(self.variationLine)

        self.descriptionTextEdit = QTextEdit()

        horizontalLayout5 = QHBoxLayout()
        locationLabel = QLabel("Location:")
        self.locationLine = QLineEdit()
        self.locationButton = QPushButton("...")
        self.locationButton.clicked.connect(self.locateFile)


        horizontalLayout5.addWidget(locationLabel)
        horizontalLayout5.addWidget(self.locationLine)
        horizontalLayout5.addWidget(self.locationButton)

        horizontalLayout6 = QHBoxLayout()
        self.slider = QSlider(Qt.Horizontal)
        self.slider.sliderReleased.connect(self.playSlider)
        self.slider.setStyleSheet("QSlider::handle:horizontal { border: 1px solid #777; background:#b55858;}")
        horizontalLayout6.addWidget(self.slider)

        horizontalLayout7 = QHBoxLayout()
        self.playButton = QPushButton()
        self.stopButton = QPushButton()

        self.playButton.setIcon(self.PLAY_ICON)
        self.stopButton.setIcon(self.STOP_ICON)

        self.playButton.clicked.connect(self.playSong)
        self.stopButton.clicked.connect(self.stopSong)

        horizontalLayout7.addStretch(1)
        horizontalLayout7.addWidget(self.playButton)
        horizontalLayout7.addWidget(self.stopButton)
        horizontalLayout7.addStretch(1)


        horizontalLayout8 = QHBoxLayout()
        self.saveButton = QPushButton()
        self.saveButton.setText("Save")
        self.saveButton.clicked.connect(self.saveSong)

        horizontalLayout8.addStretch(1)
        horizontalLayout8.addWidget(self.saveButton)

        mainLayout.addLayout(horizontalLayout1)
        mainLayout.addLayout(horizontalLayout2)
        mainLayout.addWidget(self.styleGroupBox)
        mainLayout.addLayout(horizontalLayout3)
        mainLayout.addLayout(horizontalLayout4)
        mainLayout.addWidget(self.descriptionTextEdit)
        mainLayout.addLayout(horizontalLayout5)
        mainLayout.addLayout(horizontalLayout6)
        mainLayout.addLayout(horizontalLayout7)
        mainLayout.addLayout(horizontalLayout8)

    def clearForm(self):
        self.titleEdit.clear()
        self.statusBox.clear()
        for widget in self.styleGroupBox.children():
            if not isinstance(widget, QGridLayout):
                widget.deleteLater()

        self.durationLine.clear()
        self.projectComboBox.clear()
        self.variationLine.clear()
        self.descriptionTextEdit.clear()
        self.locationLine.clear()

    def changePage(self, index):
        title = self.songsListWidget.item(index).data(Qt.UserRole)
        self.clearForm()
        self.populateForm(title)
        self.slider.setValue(0)

    def populateForm(self, title): #title is the primary key
        listArray = queries("""SELECT title, status, style, duration, description,
        location, project, variation_another_song, timestamp from songs WHERE title = ?""", (title,))
        print(listArray)
        if len(listArray) != 0:
            title = listArray[0][0]
            status = listArray[0][1]

            styles = []
            styleArray = listArray[0][2]
            if styleArray != None:
                if "," in styleArray:
                    styles = styleArray.split(",")
                else:
                    styles.append(styleArray)
            duration = listArray[0][3]
            description = listArray[0][4]
            location = listArray[0][5]
            project = listArray[0][6]
            variation_another_song = listArray[0][7]
            timestamp = listArray[0][8]
        else:
            title = None
            status = None
            styles = None
            duration = None
            description = None
            location = None
            project = None
            variation_another_song = None
            timestamp = None

        if title != None: self.titleEdit.setText(title)

        self.statusBox.addItems(["Select...", "Demo", "WIP", "Idea", "Unfinished song", "EQ", "Master", "Finished"])
        if status != None: self.statusBox.setCurrentText(status)
        if timestamp != None: self.dateEdit.setDateTime(datetime.strptime(timestamp, '%d/%m/%Y %H:%M'))
        else: self.dateEdit.setDateTime(datetime.now())#default

        styleArray = queries("select style from songs where style is not null")

        """
        print(styleArray)
        if styleArray != None:
            styleArray = styleArray[0][0]
            if "," in styleArray:
                styles = styleArray.split(",")
            else:
                styles.append(styleArray)"""

        stylesArray = []

        query = queries("select style from songs where style is not null")
        if len(query) != 0:
            for style in query:
                stylesMiniArray = style[0].split(",")
                stylesMiniArray = list(filter(None, stylesMiniArray))
                for item in stylesMiniArray:
                    if item not in stylesArray:
                        if item != '':
                            stylesArray.append(item)

        self.x = 0
        self.y = 0

        if len(stylesArray) != 0:
            for style in stylesArray:
                    print("style", style)
                    checkBox = QCheckBox(style)
                    self.styleLayout.addWidget(checkBox, self.x, self.y)
                    self.checkBoxPositionAsignment()
        self.addStyle()

        if styles!= None:
            if len(styles) != 0:
                for style in styles:
                    for checkbox in self.styleGroupBox.children():
                        if isinstance(checkbox, QCheckBox):
                            if checkbox.text() == style:
                                checkbox.setChecked(True)

        if duration != None:
                time = QTime(0,0,0)
                self.durationLine.setTime(time.addSecs(duration))

        projectsArray = ["Select..."]
        projectsArrayQuery = queries("SELECT project from songs")
        if len(projectsArrayQuery) != 0:
            for project in projectsArrayQuery[0]:
                if project not in projectsArray:
                    projectsArray.append(project)
        if project != None: self.projectComboBox.setCurrentText(project)

        if variation_another_song != None: self.variationLine.setText(variation_another_song)
        if description != None: self.descriptionTextEdit.setText(description)

        available = False
        if location != None:
            self.locationLine.setText(location)
        if len(self.locationLine.text()) != 0:
            try:
                self.playlist = QMediaPlaylist()
                self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(location)))
                self.mediaPlayer.setPlaylist(self.playlist)
            except:
                pass
            available = True#I know this is stupid but just in case

        self.slider.setVisible(available)
        self.playButton.setVisible(available)
        self.stopButton.setVisible(available)

    def populateList(self, locatorItem=None, locatorColumn=None):
        print(locatorItem, locatorColumn)
        self.songsListWidget.blockSignals(True)
        self.songsListWidget.clear()
        if locatorItem == None or locatorItem == "":
            listArray = queries("""SELECT title, status, timestamp from songs """)
            print(listArray)
        else:
            if locatorColumn != "All": #No strings concatenation, no security holes
                if locatorColumn == "Title":
                    sql = """SELECT title, status, timestamp from songs where title LIKE ?"""
                elif locatorColumn == "Status":
                    sql = """SELECT title, status, timestamp from songs where status LIKE ?"""
                elif locatorColumn == "Description":
                    sql = """SELECT title, status, timestamp from songs where description LIKE ?"""
                elif locatorColumn == "Style":
                    sql = """SELECT title, status, timestamp from songs where style LIKE ?"""

                locatorItem = "%" + locatorItem + "%"
                listArray = queries(sql, (locatorItem,))
            else:
                locatorItem = "%" + locatorItem + "%"
                variables = [locatorItem, locatorItem, locatorItem, locatorItem, locatorItem]
                listArray = queries("""SELECT title, status, timestamp from songs
                where title LIKE ? OR type LIKE ? OR original_song LIKE ? OR link LIKE ?
                OR description LIKE ?""", variables)
        for item in listArray:
            title = item[0]
            status = item[1]
            timestamp = item[2]
            try:
                timestamp = datetime.strptime(timestamp, "%d/%m/%Y %H:%M")
                timestamp = timestamp.strftime("%d/%m/%Y")
            except:
                timestamp = ""

            text = "%s %s %s" % (title, status, timestamp)
            qItem = QListWidgetItem(text)
            qItem.setData(Qt.UserRole, title)
            self.songsListWidget.addItem(qItem)
        #new idea
        qItem = QListWidgetItem("New song...")
        qItem.setData(Qt.UserRole, "New song...") #otherwise that would be an error
        self.songsListWidget.addItem(qItem)
        self.songsListWidget.blockSignals(False)

    def listWidgetRightClick(self, position):
        widgetItem = self.songsListWidget.itemAt(position)
        if widgetItem != None: #quick lazy text fix
            if widgetItem.text() != "New song...":
                print(widgetItem.text())
                menu = QMenu()
                deleteAction = QAction(self.DELETE_ICON, "Delete song")
                menu.addAction(deleteAction)
                action = menu.exec(self.mapToGlobal(position)) 

                if action == deleteAction:
                    msg = QMessageBox.question(None, "Delete?", "Are you sure you want to delete this entry?")
                    if msg == QMessageBox.Yes:
                        title = widgetItem.data(Qt.UserRole)
                        queries("DELETE from songs where title = ?", (title,))
                        self.populateList()
                        self.songsListWidget.setCurrentRow(0)

    def songVariations(self):
        sql = "SELECT title from songs"
        songArray = []
        for song in queries(sql)[0]:
            songArray.append(song)
        return songArray
    def checkBoxPositionAsignment(self):
            self.y += 1
            if self.y == 4:
                self.y = 0
                self.x += 1
    def addStyle(self, text=""):
        "text = "" if comes from outside"

        self.styleEdit = QLineEdit()
        self.styleEdit.setPlaceholderText("Style")
        self.styleEdit.textChanged.connect(self.validateStyle)
        self.styleEdit.returnPressed.connect(lambda: self.addStyle(self.styleEdit.text()))

        if text != "":
            self.styleLayout.takeAt(self.styleLayout.count()-1).widget().deleteLater()

            styleCheckBox = QCheckBox()
            styleCheckBox.setText(text)
            print(text)
            self.styleLayout.addWidget(styleCheckBox, self.x, self.y)
            self.checkBoxPositionAsignment()
            print(self.durationLine.text())
        self.styleLayout.addWidget(self.styleEdit)


    def checkSong(self):
        text = self.titleEdit.text()
        sql = "SELECT title from songs where title = ?"
        if len(queries(sql, (text,))) != 0:
            self.titleEdit.setText("")
    def validateSong(self):
        pass
        #VALIDATE REG EXP

    def validateStyle(self, text):
        if "," in text:
            self.styleEdit.undo()

    def playSong(self):
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()
        else:
            self.mediaPlayer.play()

    def mediaStateChanged(self):
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.playButton.setIcon(self.PAUSE_ICON)
        else:
            self.playButton.setIcon(self.PLAY_ICON)
    def positionChanged(self, position):
        if position != self.mediaPlayer.duration():
            self.slider.setValue(position)
    def durationChanged(self, duration):
        if duration != self.mediaPlayer.position():
            print("duration chagned")
            self.slider.setRange(0, duration)
    def playSlider(self):
        self.mediaPlayer.setPosition(self.slider.value())

    def stopSong(self):
        self.mediaPlayer.stop()

    def locateFile(self):
        self.fileSystem = QFileDialog(filter="Sound files (*.wav *.mp3 *.flac)")
        self.fileSystem.show()
        self.fileSystem.fileSelected.connect(self.fileLoaded)

    def fileLoaded(self, path):
        self.locationLine.setText(path)
        try:
            self.playlist = QMediaPlaylist()
            self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(path)))
            self.mediaPlayer.setPlaylist(self.playlist)
        except:
            print("fail")
        self.slider.setVisible(True)
        self.playButton.setVisible(True)
        self.stopButton.setVisible(True)

    def saveSong(self):
        title = self.titleEdit.text()
        status = self.statusBox.currentText()
        date = self.dateEdit.text()
        style = ""

        print(status, style)
        x = 0
        for checkBox in self.styleGroupBox.children():
            if isinstance(checkBox, QCheckBox):
                if checkBox.isChecked():
                    style += (checkBox.text()) + ","
                    x+=1
        if x != 0: style = style.rstrip(",")
        else: style = None

        duration = self.durationLine.time()
        duration = QTime(0, 0).secsTo(duration)

        project = self.projectComboBox.currentText()
        variation = self.variationLine.text()
        description = self.descriptionTextEdit.toPlainText()
        location = self.locationLine.text()

        variables = [title, status, description, location, project,\
        variation, date, style, duration]

        print("---------", variables)

        sql = """INSERT OR REPLACE into songs
        (title, status, description, location, project,
        variation_another_song, timestamp, style, duration)

        values
        (?,      ?,       ?,          ?,     ?,
         ?,                     ?,         ?,      ?)"""

        queries(sql, variables)
        self.populateList()
Exemple #21
0
class QgsFmvPlayer(QMainWindow, Ui_PlayerWindow):
    """ Video Player Class """
    def __init__(self, iface, path=None, parent=None):
        """ Constructor """
        super(QgsFmvPlayer, self).__init__(parent)
        self.setupUi(self)
        self.parent = parent
        self.iface = iface
        self.fileName = None
        self.metadataDlg = None
        self.createingMosaic = False
        self.currentInfo = 0.0

        self.RecGIF = QMovie(":/imgFMV/images/record.gif")

        self.videoWidget.customContextMenuRequested[QPoint].connect(
            self.contextMenuRequested)

        self.duration = 0
        self.playerMuted = False
        self.HasFileAudio = False

        self.player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.player.setNotifyInterval(1000)  # One second
        self.pass_time = 0.1
        self.playlist = QMediaPlaylist()

        #         self.player.setVideoOutput(
        #             self.videoWidget)  # Standar Surface

        self.player.setVideoOutput(
            self.videoWidget.videoSurface())  # Custom Surface

        self.player.durationChanged.connect(self.durationChanged)
        self.player.positionChanged.connect(self.positionChanged)
        self.player.mediaStatusChanged.connect(self.statusChanged)

        self.player.stateChanged.connect(self.setCurrentState)

        self.playerState = QMediaPlayer.StoppedState
        self.playFile(path)

        self.sliderDuration.setRange(0, self.player.duration() / 1000)

        self.volumeSlider.setValue(self.player.volume())
        self.volumeSlider.enterEvent = self.showVolumeTip

        if self.metadataDlg is None:
            self.metadataDlg = QgsFmvMetadata(parent=self, player=self)
            self.addDockWidget(Qt.RightDockWidgetArea, self.metadataDlg)
            self.metadataDlg.setMinimumWidth(500)
            self.metadataDlg.hide()

    def HasMetadata(self, videoPath):
        """ Check if video have Metadata or not """
        try:
            p = _spawn([
                '-i', videoPath, '-map', 'data-re', '-codec', 'copy', '-f',
                'data', '-'
            ])

            stdout_data, _ = p.communicate()

            if stdout_data == b'':
                qgsu.showUserAndLogMessage(QCoreApplication.translate(
                    "QgsFmvPlayer", "This video don't have Metadata ! : "),
                                           level=QGis.Info)
                return False

            return True
        except Exception as e:
            qgsu.showUserAndLogMessage(QCoreApplication.translate(
                "QgsFmvPlayer", "Metadata Callback Failed! : "),
                                       str(e),
                                       level=QGis.Info)

    def HasAudio(self, videoPath):
        """ Check if video have Metadata or not """
        try:
            p = _spawn([
                '-i', videoPath, '-show_streams', '-select_streams', 'a',
                '-loglevel', 'error'
            ],
                       type="ffprobe")

            stdout_data, _ = p.communicate()

            if stdout_data == b'':
                qgsu.showUserAndLogMessage(QCoreApplication.translate(
                    "QgsFmvPlayer", "This video don't have Audio ! : "),
                                           level=QGis.Info)
                return False

            return True
        except Exception as e:
            qgsu.showUserAndLogMessage(QCoreApplication.translate(
                "QgsFmvPlayer", "Audio check Failed! : "),
                                       str(e),
                                       level=QGis.Info)

    def callBackMetadata(self, currentTime, nextTime):
        """ Metadata CallBack """
        try:
            # TODO : Speed this function
            #             stdout_data = _check_output(['-i', self.fileName,
            #                         '-ss', currentTime,
            #                         '-to', nextTime,
            #                         '-f', 'data', '-'])

            t = callBackMetadataThread(cmds=[
                '-i', self.fileName, '-ss', currentTime, '-to', nextTime,
                '-map', 'data-re', '-f', 'data', '-'
            ])
            t.start()
            t.join(1)
            if t.is_alive():
                t.p.terminate()
                t.join()
            if t.stdout == b'':
                return

            for packet in StreamParser(t.stdout):
                try:
                    self.addMetadata(packet.MetadataList())
                    UpdateLayers(packet,
                                 parent=self,
                                 mosaic=self.createingMosaic)
                    self.iface.mapCanvas().refresh()
                    QApplication.processEvents()
                    return
                except Exception as e:
                    None
        except Exception as e:
            qgsu.showUserAndLogMessage(QCoreApplication.translate(
                "QgsFmvPlayer", "Metadata Callback Failed! : "),
                                       str(e),
                                       level=QGis.Info)

    def addMetadata(self, packet):
        ''' Add Metadata to List '''
        self.clearMetadata()
        row = 0
        for key in sorted(packet.keys()):
            self.metadataDlg.VManager.insertRow(row)
            self.metadataDlg.VManager.setItem(row, 0,
                                              QTableWidgetItem(str(key)))
            self.metadataDlg.VManager.setItem(
                row, 1, QTableWidgetItem(str(packet[key][0])))
            self.metadataDlg.VManager.setItem(
                row, 2, QTableWidgetItem(str(packet[key][1])))
            row += 1
        self.metadataDlg.VManager.setVisible(False)
        self.metadataDlg.VManager.resizeColumnsToContents()
        self.metadataDlg.VManager.setVisible(True)
        self.metadataDlg.VManager.verticalScrollBar().setSliderPosition(
            self.sliderPosition)

    def clearMetadata(self):
        ''' Clear Metadata List '''
        try:
            self.sliderPosition = self.metadataDlg.VManager.verticalScrollBar(
            ).sliderPosition()
            self.metadataDlg.VManager.setRowCount(0)
        except:
            None

    def saveInfoToJson(self):
        """ Save video Info to json """
        if not self.KillAllProcessors():
            return
        out_json, _ = QFileDialog.getSaveFileName(self, "Save File", "",
                                                  "Json Files (*.json)")
        if out_json == "":
            return
        try:
            self.VPProbeToJson = Converter()
            self.VPTProbeToJson = QThread()

            self.VPProbeToJson.moveToThread(self.VPTProbeToJson)

            self.VPProbeToJson.finished.connect(self.QThreadFinished)

            self.VPProbeToJson.error.connect(self.QThreadError)

            self.VPProbeToJson.progress.connect(
                self.progressBarProcessor.setValue)

            self.VPTProbeToJson.start(QThread.LowPriority)

            QMetaObject.invokeMethod(self.VPProbeToJson, 'probeToJson',
                                     Qt.QueuedConnection,
                                     Q_ARG(str, self.fileName),
                                     Q_ARG(str, out_json))

        except Exception as e:
            qgsu.showUserAndLogMessage(
                QCoreApplication.translate("QgsFmvPlayer",
                                           "Error saving Json"))
            self.QThreadFinished("probeToJson", "Closing ProbeToJson")

    def showVideoInfo(self):
        ''' Show default probe info '''
        try:

            self.VPProbe = Converter()
            self.VPTProbe = QThread()

            self.VPProbe.moveToThread(self.VPTProbe)

            self.VPProbe.finishedJson.connect(self.QThreadFinished)

            self.VPProbe.error.connect(self.QThreadError)

            self.VPProbe.progress.connect(self.progressBarProcessor.setValue)

            self.VPTProbe.start(QThread.LowPriority)

            QMetaObject.invokeMethod(self.VPProbe, 'probeShow',
                                     Qt.QueuedConnection,
                                     Q_ARG(str, self.fileName))

        except Exception as e:
            qgsu.showUserAndLogMessage(
                QCoreApplication.translate("QgsFmvPlayer", "Error Info Show"))
            self.QThreadFinished("probeShow", "Closing Probe")
        return

    def state(self):
        ''' Return Current State '''
        return self.playerState

    def setCurrentState(self, state):
        ''' Set Current State '''
        if state != self.playerState:
            self.playerState = state

            if state == QMediaPlayer.StoppedState:
                self.btn_play.setIcon(QIcon(":/imgFMV/images/play-arrow.png"))

        return

    def showColorDialog(self):
        ''' Show Color dialog '''
        self.ColorDialog = ColorDialog(parent=self)
        self.ColorDialog.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint)
        # Fail if not uncheked
        self.actionMagnifying_glass.setChecked(False)
        self.actionZoom_Rectangle.setChecked(False)
        self.ColorDialog.exec_()
        return

    def createMosaic(self, value):
        ''' Function for create Video Mosaic '''
        home = os.path.expanduser("~")

        qgsu.createFolderByName(home, "QGIS_FMV")
        homefmv = os.path.join(home, "QGIS_FMV")
        root, ext = os.path.splitext(os.path.basename(self.fileName))
        qgsu.createFolderByName(homefmv, root)
        self.createingMosaic = value
        # Create Group
        CreateGroupByName()
        return

    def contextMenuRequested(self, point):
        ''' Context Menu Video '''
        menu = QMenu()

        #         actionColors = menu.addAction(
        #             QCoreApplication.translate("QgsFmvPlayer", "Color Options"))
        #         actionColors.setShortcut("Ctrl+May+C")
        #         actionColors.triggered.connect(self.showColorDialog)

        actionMute = menu.addAction(
            QCoreApplication.translate("QgsFmvPlayer", "Mute/Unmute"))
        actionMute.setShortcut("Ctrl+May+U")
        actionMute.triggered.connect(self.setMuted)

        menu.addSeparator()
        actionAllFrames = menu.addAction(
            QCoreApplication.translate("QgsFmvPlayer", "Extract All Frames"))
        actionAllFrames.setShortcut("Ctrl+May+A")
        actionAllFrames.triggered.connect(self.ExtractAllFrames)

        actionCurrentFrames = menu.addAction(
            QCoreApplication.translate("QgsFmvPlayer",
                                       "Extract Current Frame"))
        actionCurrentFrames.setShortcut("Ctrl+May+Q")
        actionCurrentFrames.triggered.connect(self.ExtractCurrentFrame)

        menu.addSeparator()
        actionShowMetadata = menu.addAction(
            QCoreApplication.translate("QgsFmvPlayer", "Show Metadata"))
        actionShowMetadata.setShortcut("Ctrl+May+M")
        actionShowMetadata.triggered.connect(self.OpenQgsFmvMetadata)

        menu.exec_(self.mapToGlobal(point))

    # Start Snnipet FILTERS
    def grayFilter(self, value):
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetGray(value)
        self.videoWidget.UpdateSurface()
        return

    def edgeFilter(self, value):
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetEdgeDetection(value)
        self.videoWidget.UpdateSurface()
        return

    def invertColorFilter(self, value):
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetInvertColor(value)
        self.videoWidget.UpdateSurface()
        return

    def autoContrastFilter(self, value):
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetAutoContrastFilter(value)
        self.videoWidget.UpdateSurface()
        return

    def monoFilter(self, value):
        self.UncheckFilters(self.sender(), value)
        self.videoWidget.SetMonoFilter(value)
        self.videoWidget.UpdateSurface()
        return

    def magnifier(self, value):
        self.UncheckUtils(self.sender(), value)
        self.videoWidget.SetMagnifier(value)
        self.videoWidget.UpdateSurface()
        return

    def zoomRect(self, value):
        self.UncheckUtils(self.sender(), value)
        self.videoWidget.SetZoomRect(value)
        self.videoWidget.UpdateSurface()
        return

    def UncheckUtils(self, sender, value):
        #         p = self.player.position()
        #         self.player.setVideoOutput(
        #             self.videoWidget.videoSurface())  # Custom surface
        #         self.player.setPosition(p)
        QApplication.processEvents()
        name = sender.objectName()
        self.actionMagnifying_glass.setChecked(
            True if name == "actionMagnifying_glass" else False)
        self.actionZoom_Rectangle.setChecked(True if name ==
                                             "actionZoom_Rectangle" else False)

        sender.setChecked(value)
        return

    def UncheckFilters(self, sender, value):
        #         p = self.player.position()
        #         self.player.setVideoOutput(
        #             self.videoWidget.videoSurface())  # Custom surface
        #         self.player.setPosition(p)
        #         QApplication.processEvents()
        name = sender.objectName()

        self.actionGray.setChecked(True if name == "actionGray" else False)
        self.actionInvert_Color.setChecked(True if name ==
                                           "actionInvert_Color" else False)
        self.actionMono_Filter.setChecked(True if name ==
                                          "actionMono_Filter" else False)
        self.actionCanny_edge_detection.setChecked(
            True if name == "actionCanny_edge_detection" else False)
        self.actionAuto_Contrast_Filter.setChecked(
            True if name == "actionAuto_Contrast_Filter" else False)

        self.videoWidget.SetGray(True if name == "actionGray" else False)
        self.videoWidget.SetEdgeDetection(
            True if name == "actionCanny_edge_detection" else False)
        self.videoWidget.SetInvertColor(True if name ==
                                        "actionInvert_Color" else False)
        self.videoWidget.SetMonoFilter(True if name ==
                                       "actionMono_Filter" else False)
        self.videoWidget.SetAutoContrastFilter(
            True if name == "actionAuto_Contrast_Filter" else False)

        sender.setChecked(value)
        return

    # End Snnipet FILTERS

    def isMuted(self):
        ''' Is muted video property'''
        return self.playerMuted

    def setMuted(self):
        ''' Muted video '''
        if self.player.isMuted():
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_up.png"))
            self.player.setMuted(False)
            self.volumeSlider.setEnabled(True)
        else:
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_off.png"))
            self.player.setMuted(True)
            self.volumeSlider.setEnabled(False)
        return

    def stop(self):
        ''' Stop video'''
        self.player.stop()
        self.videoWidget.update()
        return

    def volume(self):
        ''' Volume Slider '''
        return self.volumeSlider.value()

    def setVolume(self, volume):
        ''' Tooltip and set value'''
        self.player.setVolume(volume)
        self.showVolumeTip(volume)
        if 0 < volume <= 30:
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_30.png"))
        elif 30 < volume <= 60:
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_60.png"))
        elif 60 < volume <= 100:
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_up.png"))
        elif volume == 0:
            self.btn_volume.setIcon(QIcon(":/imgFMV/images/volume_off.png"))

    def EndMedia(self):
        ''' Button end video position '''
        if self.player.isVideoAvailable():
            self.player.setPosition(self.player.duration())
            self.videoWidget.update()
        return

    def StartMedia(self):
        ''' Button start video position '''
        if self.player.isVideoAvailable():
            self.player.setPosition(0)
            self.videoWidget.update()
        return

    def forwardMedia(self):
        ''' Button forward Video '''
        forwardTime = int(self.player.position()) + 10 * 1000
        if forwardTime > int(self.player.duration()):
            forwardTime = int(self.player.duration())
        self.player.setPosition(forwardTime)

    def rewindMedia(self):
        ''' Button rewind Video '''
        rewindTime = int(self.player.position()) - 10 * 1000
        if rewindTime < 0:
            rewindTime = 0
        self.player.setPosition(rewindTime)

    def AutoRepeat(self, checked):
        ''' Button AutoRepeat Video '''
        if checked:
            self.playlist.setPlaybackMode(QMediaPlaylist.Loop)
        else:
            self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)
        return

    def showVolumeTip(self, _):
        ''' Volume Slider Tooltip Trick '''
        self.style = self.volumeSlider.style()
        self.opt = QStyleOptionSlider()
        self.volumeSlider.initStyleOption(self.opt)
        rectHandle = self.style.subControlRect(self.style.CC_Slider, self.opt,
                                               self.style.SC_SliderHandle)
        self.tip_offset = QPoint(5, 15)
        pos_local = rectHandle.topLeft() + self.tip_offset
        pos_global = self.volumeSlider.mapToGlobal(pos_local)
        QToolTip.showText(pos_global,
                          str(self.volumeSlider.value()) + " %", self)

    def showMoveTip(self, currentInfo):
        ''' Player Silder Move Tooptip Trick '''
        self.style = self.sliderDuration.style()
        self.opt = QStyleOptionSlider()
        self.sliderDuration.initStyleOption(self.opt)
        rectHandle = self.style.subControlRect(self.style.CC_Slider, self.opt,
                                               self.style.SC_SliderHandle)
        self.tip_offset = QPoint(5, 15)
        pos_local = rectHandle.topLeft() + self.tip_offset
        pos_global = self.sliderDuration.mapToGlobal(pos_local)

        tStr = _seconds_to_time(currentInfo)

        QToolTip.showText(pos_global, tStr, self)

    def durationChanged(self, duration):
        ''' Duration video change signal '''
        duration /= 1000
        self.duration = duration
        self.sliderDuration.setMaximum(duration)

    def positionChanged(self, progress):
        ''' Current Video position change '''
        progress /= 1000

        if not self.sliderDuration.isSliderDown():
            self.sliderDuration.setValue(progress)

        self.updateDurationInfo(progress)

    def updateDurationInfo(self, currentInfo):
        ''' Update labels duration Info and CallBack Metadata '''
        duration = self.duration
        self.currentInfo = currentInfo
        if currentInfo or duration:

            totalTime = _seconds_to_time(duration)
            currentTime = _seconds_to_time(currentInfo)
            tStr = currentTime + " / " + totalTime

            nextTime = currentInfo + self.pass_time
            currentTimeInfo = _seconds_to_time_frac(currentInfo)
            nextTimeInfo = _seconds_to_time_frac(nextTime)
            # Metadata CallBack
            self.callBackMetadata(currentTimeInfo, nextTimeInfo)

        else:
            tStr = ""

        self.labelDuration.setText(tStr)

    def handleCursor(self, status):
        ''' Change cursor '''
        if status in (QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia,
                      QMediaPlayer.StalledMedia):
            self.setCursor(Qt.BusyCursor)
        else:
            self.unsetCursor()

    def statusChanged(self, status):
        ''' Signal Status video change '''
        self.handleCursor(status)
        if status == QMediaPlayer.LoadingMedia:
            self.videoAvailableChanged(False)
        elif status == QMediaPlayer.StalledMedia:
            self.videoAvailableChanged(False)
        if status == QMediaPlayer.EndOfMedia:
            self.videoAvailableChanged(True)
        elif status == QMediaPlayer.InvalidMedia:
            qgsu.showUserAndLogMessage(QCoreApplication.translate(
                "QgsFmvPlayer", self.player.errorString()),
                                       level=QGis.Warning)
            self.videoAvailableChanged(False)
        else:
            self.videoAvailableChanged(True)

    def playFile(self, videoPath):
        ''' Play file from path '''
        try:
            RemoveVideoLayers()
            RemoveGroupByName()
            self.fileName = videoPath
            self.playlist = QMediaPlaylist()
            url = QUrl.fromLocalFile(videoPath)
            self.playlist.addMedia(QMediaContent(url))
            self.playlist.setPlaybackMode(QMediaPlaylist.Sequential)
            self.player.setPlaylist(self.playlist)

            self.setWindowTitle("Playing : " +
                                os.path.basename(os.path.normpath(videoPath)))

            if self.HasMetadata(videoPath):
                CreateVideoLayers()
                self.clearMetadata()
                self.lb_cursor_coord.setText(
                    "<span style='font-size:10pt; font-weight:bold;'>Lon :</span>"
                    +
                    "<span style='font-size:9pt; font-weight:normal;'>Null</span>"
                    +
                    "<span style='font-size:10pt; font-weight:bold;'> Lat :</span>"
                    +
                    "<span style='font-size:9pt; font-weight:normal;'>Null</span>"
                )
            else:
                self.btn_GeoReferencing.setEnabled(False)

            self.HasFileAudio = True
            if not self.HasAudio(videoPath):
                self.actionAudio.setEnabled(False)
                self.actionSave_Audio.setEnabled(False)
                self.HasFileAudio = False

            self.playClicked(True)

        except Exception as e:
            qgsu.showUserAndLogMessage(QCoreApplication.translate(
                "QgsFmvPlayer", 'Open Video File : '),
                                       str(e),
                                       level=QGis.Warning)

    def ReciconUpdate(self, frame):
        self.btn_Rec.setIcon(QIcon(self.RecGIF.currentPixmap()))

    def RecordVideo(self, value):
        ''' Cut Video '''
        currentTime = _seconds_to_time(self.currentInfo)

        if value is False:
            self.endRecord = currentTime
            _, file_extension = os.path.splitext(self.fileName)
            out, _ = QFileDialog.getSaveFileName(self, "Save As", "",
                                                 file_extension)
            if not out:
                self.RecGIF.frameChanged.disconnect(self.ReciconUpdate)
                self.RecGIF.stop()
                self.btn_Rec.setIcon(QIcon(":/imgFMV/images/record.png"))
                return False

            lfn = out.lower()
            if not lfn.endswith((file_extension)):
                out += file_extension

            p = _spawn([
                '-i', self.fileName, '-ss', self.startRecord, '-to',
                self.endRecord, '-c', 'copy', out
            ])
            p.communicate()
            qgsu.showUserAndLogMessage(
                QCoreApplication.translate("QgsFmvPlayer",
                                           "Save file succesfully!"))

            self.RecGIF.frameChanged.disconnect(self.ReciconUpdate)
            self.RecGIF.stop()
            self.btn_Rec.setIcon(QIcon(":/imgFMV/images/record.png"))
        else:
            self.startRecord = currentTime
            self.RecGIF.frameChanged.connect(self.ReciconUpdate)
            self.RecGIF.start()
        return

    def videoAvailableChanged(self, available):
        ''' Buttons for video available '''
        # self.btn_Color.setEnabled(available)
        self.btn_CaptureFrame.setEnabled(available)
        self.gb_PlayerControls.setEnabled(available)
        return

    def toggleGroup(self, state):
        ''' Toggle GroupBox '''
        sender = self.sender()
        if state:
            sender.setFixedHeight(sender.sizeHint().height())
        else:
            sender.setFixedHeight(15)

    def playClicked(self, state):
        ''' Stop and Play video '''
        if self.playerState in (QMediaPlayer.StoppedState,
                                QMediaPlayer.PausedState):
            self.btn_play.setIcon(QIcon(":/imgFMV/images/pause.png"))
            self.player.play()
        elif self.playerState == QMediaPlayer.PlayingState:
            self.btn_play.setIcon(QIcon(":/imgFMV/images/play-arrow.png"))
            self.player.pause()

    def seek(self, seconds):
        '''Slider Move'''
        self.player.setPosition(seconds * 1000)
        self.showMoveTip(seconds)

    def convertVideo(self):
        '''Convert Video To Other Format '''
        if not self.KillAllProcessors():
            return
        sel = "mp4 Files (*.mp4)"
        out, _ = QFileDialog.getSaveFileName(
            self, "Save Video as...", None,
            "ogg files (*.ogg);;avi Files (*.avi);;mkv Files (*.mkv);;webm Files (*.webm);;flv Files (*.flv);;mov Files (*.mov);;mp4 Files (*.mp4);;mpg Files (*.mpg);;mp3 Files (*.mp3)",
            sel)

        if not out:
            return False

        lfn = out.lower()
        if not lfn.endswith(('.ogg', '.avi', '.mkv', '.webm', '.flv', '.mov',
                             '.mp4', '.mp3', '.mpg')):
            # The default.
            out += '.mp4'

        try:
            self.VPConverter = Converter()
            self.VPTConverter = QThread()

            self.VPConverter.moveToThread(self.VPTConverter)

            self.VPConverter.finished.connect(self.QThreadFinished)

            self.VPConverter.error.connect(self.QThreadError)

            self.VPConverter.progress.connect(
                self.progressBarProcessor.setValue)

            self.VPTConverter.start(QThread.LowPriority)

            # TODO : Make Correct format Conversion and embebed metadata
            info = self.VPConverter.probeInfo(self.fileName)
            if info is not None:
                if self.HasFileAudio:
                    audio_codec = info.audio.codec
                    audio_samplerate = info.audio.audio_samplerate
                    audio_channels = info.audio.audio_channels

                video_codec = info.video.codec
                video_width = info.video.video_width
                video_height = info.video.video_height
                video_fps = info.video.video_fps

            _, out_ext = os.path.splitext(out)

            if self.HasFileAudio:
                options = {
                    'format': out_ext[1:],
                    'audio': {
                        'codec': audio_codec,
                        'samplerate': audio_samplerate,
                        'channels': audio_channels
                    },
                    'video': {
                        'codec': video_codec,
                        'width': video_width,
                        'height': video_height,
                        'fps': video_fps
                    }
                }
            else:
                options = {
                    'format': out_ext[1:],
                    'video': {
                        'codec': video_codec,
                        'width': video_width,
                        'height': video_height,
                        'fps': video_fps
                    }
                }
            QMetaObject.invokeMethod(self.VPConverter, 'convert',
                                     Qt.QueuedConnection,
                                     Q_ARG(str,
                                           self.fileName), Q_ARG(str, out),
                                     Q_ARG(dict, options), Q_ARG(bool, False))

        except Exception as e:
            qgsu.showUserAndLogMessage(
                QCoreApplication.translate("QgsFmvPlayer",
                                           "Error converting video "))
            self.QThreadFinished("convert", "Closing convert")

    def ShowPlot(self, bitrate_data, frame_count, output=None):
        ''' Show plot,because show not work using threading '''
        matplot.figure().canvas.set_window_title(self.fileName)
        matplot.title("Stream Bitrate vs Time")
        matplot.xlabel("Time (sec)")
        matplot.ylabel("Frame Bitrate (kbit/s)")
        matplot.grid(True)
        # map frame type to color
        frame_type_color = {
            # audio
            'A': 'yellow',
            # video
            'I': 'red',
            'P': 'green',
            'B': 'blue'
        }

        global_peak_bitrate = 0.0
        global_mean_bitrate = 0.0

        # render charts in order of expected decreasing size
        for frame_type in ['I', 'P', 'B', 'A']:

            # skip frame type if missing
            if frame_type not in bitrate_data:
                continue

            # convert list of tuples to numpy 2d array
            frame_list = bitrate_data[frame_type]
            frame_array = numpy.array(frame_list)

            # update global peak bitrate
            peak_bitrate = frame_array.max(0)[1]
            if peak_bitrate > global_peak_bitrate:
                global_peak_bitrate = peak_bitrate

            # update global mean bitrate (using piecewise mean)
            mean_bitrate = frame_array.mean(0)[1]
            global_mean_bitrate += mean_bitrate * \
                (len(frame_list) / frame_count)

            # plot chart using gnuplot-like impulses
            matplot.vlines(frame_array[:, 0], [0],
                           frame_array[:, 1],
                           color=frame_type_color[frame_type],
                           label="{} Frames".format(frame_type))

        self.progressBarProcessor.setValue(90)
        # calculate peak line position (left 15%, above line)
        peak_text_x = matplot.xlim()[1] * 0.15
        peak_text_y = global_peak_bitrate + \
            ((matplot.ylim()[1] - matplot.ylim()[0]) * 0.015)
        peak_text = "peak ({:.0f})".format(global_peak_bitrate)

        # draw peak as think black line w/ text
        matplot.axhline(global_peak_bitrate, linewidth=2, color='black')
        matplot.text(peak_text_x,
                     peak_text_y,
                     peak_text,
                     horizontalalignment='center',
                     fontweight='bold',
                     color='black')

        # calculate mean line position (right 85%, above line)
        mean_text_x = matplot.xlim()[1] * 0.85
        mean_text_y = global_mean_bitrate + \
            ((matplot.ylim()[1] - matplot.ylim()[0]) * 0.015)
        mean_text = "mean ({:.0f})".format(global_mean_bitrate)

        # draw mean as think black line w/ text
        matplot.axhline(global_mean_bitrate, linewidth=2, color='black')
        matplot.text(mean_text_x,
                     mean_text_y,
                     mean_text,
                     horizontalalignment='center',
                     fontweight='bold',
                     color='black')

        matplot.legend()
        if output != "":
            matplot.savefig(output)
        else:
            matplot.show()

        self.progressBarProcessor.setValue(100)

    def CreateBitratePlot(self):
        ''' Create video Plot Bitrate Thread '''
        if not self.KillAllProcessors():
            return
        try:
            self.VPBitratePlot = CreatePlotsBitrate()
            self.VPTBitratePlot = QThread()

            self.VPBitratePlot.moveToThread(self.VPTBitratePlot)

            self.VPBitratePlot.finished.connect(self.QThreadFinished)

            self.VPBitratePlot.return_fig.connect(self.ShowPlot)

            self.VPBitratePlot.error.connect(self.QThreadError)

            self.VPBitratePlot.progress.connect(
                self.progressBarProcessor.setValue)

            self.VPTBitratePlot.start(QThread.LowPriority)

            sender = self.sender().objectName()

            if sender == "actionAudio":
                QMetaObject.invokeMethod(self.VPBitratePlot, 'CreatePlot',
                                         Qt.QueuedConnection,
                                         Q_ARG(str, self.fileName),
                                         Q_ARG(str, None), Q_ARG(str, 'audio'))

            elif sender == "actionVideo":
                QMetaObject.invokeMethod(self.VPBitratePlot, 'CreatePlot',
                                         Qt.QueuedConnection,
                                         Q_ARG(str, self.fileName),
                                         Q_ARG(str, None), Q_ARG(str, 'video'))

            elif sender == "actionSave_Audio":
                selfilter = "Portable Network Graphics (*.png)"
                fileaudio, _ = QFileDialog.getSaveFileName(
                    self, "Save Audio Bitrate Plot", "",
                    "EPS Encapsulated Postscript (*.eps);;"
                    "PGF code for LaTex (*.pgf);;"
                    "Portable document format(*pdf);;"
                    "Portable Network Graphics (*.png);;"
                    "Postscript (*.ps);;"
                    "Raw RGBA bitmap (*.raw*.rgba);;"
                    "Scalable vector graphics (*.svg*.svgz)", selfilter)
                if fileaudio == "":
                    return

                QMetaObject.invokeMethod(self.VPBitratePlot, 'CreatePlot',
                                         Qt.QueuedConnection,
                                         Q_ARG(str, self.fileName),
                                         Q_ARG(str, fileaudio),
                                         Q_ARG(str, 'audio'))

            elif sender == "actionSave_Video":
                selfilter = "Portable Network Graphics (*.png)"
                filevideo, _ = QFileDialog.getSaveFileName(
                    self, "Save Video Bitrate Plot", "",
                    "EPS Encapsulated Postscript (*.eps);;"
                    "PGF code for LaTex (*.pgf);;"
                    "Portable document format(*pdf);;"
                    "Portable Network Graphics (*.png);;"
                    "Postscript (*.ps);;"
                    "Raw RGBA bitmap (*.raw*.rgba);;"
                    "Scalable vector graphics (*.svg*.svgz)", selfilter)
                if filevideo == "":
                    return

                QMetaObject.invokeMethod(self.VPBitratePlot, 'CreatePlot',
                                         Qt.QueuedConnection,
                                         Q_ARG(str, self.fileName),
                                         Q_ARG(str, filevideo),
                                         Q_ARG(str, 'video'))

        except Exception as e:
            qgsu.showUserAndLogMessage(
                QCoreApplication.translate("QgsFmvPlayer",
                                           "Failed creating Plot Bitrate"))

    def ExtractAllFrames(self):
        """ Extract All Video Frames Thread """
        if not self.KillAllProcessors():
            return

        options = QFileDialog.DontResolveSymlinks | QFileDialog.ShowDirsOnly
        directory = QFileDialog.getExistingDirectory(
            self,
            QCoreApplication.translate("QgsFmvPlayer", "Save images"),
            '',
            options=options)

        if directory:

            self.VPExtractFrames = ExtractFramesProcessor()
            self.VPTExtractAllFrames = QThread()

            self.VPExtractFrames.moveToThread(self.VPTExtractAllFrames)
            self.VPExtractFrames.finished.connect(self.QThreadFinished)
            self.VPExtractFrames.error.connect(self.QThreadError)
            self.VPExtractFrames.progress.connect(
                self.progressBarProcessor.setValue)
            self.VPTExtractAllFrames.start(QThread.LowPriority)

            QMetaObject.invokeMethod(self.VPExtractFrames,
                                     'ExtractFrames', Qt.QueuedConnection,
                                     Q_ARG(str, directory),
                                     Q_ARG(str, self.fileName))
        return

    def ExtractCurrentFrame(self):
        """ Extract Current Frame Thread """
        image = self.videoWidget.GetCurrentFrame()
        out_image, _ = QFileDialog.getSaveFileName(
            self, "Save Current Frame", "",
            "Image File (*.png *.jpg *.bmp *.tiff)")

        if out_image == "":
            return

        if out_image:
            t = threading.Thread(target=self.SaveCapture,
                                 args=(
                                     image,
                                     out_image,
                                 ))
            t.start()
        return

    def SaveCapture(self, image, output):
        ''' Save Current Image '''
        image.save(output)
        QApplication.processEvents()
        return

    def QThreadFinished(self, process, msg, outjson=None):
        ''' Finish Threads '''
        if process == "ExtractFramesProcessor":
            self.VPExtractFrames.deleteLater()
            self.VPTExtractAllFrames.terminate()
            self.VPTExtractAllFrames.deleteLater()
        elif process == "CreatePlotsBitrate":
            self.VPBitratePlot.deleteLater()
            self.VPTBitratePlot.terminate()
            self.VPTBitratePlot.deleteLater()
        elif process == "convert":
            self.VPConverter.deleteLater()
            self.VPTConverter.terminate()
            self.VPTConverter.deleteLater()
        elif process == "probeToJson":
            self.VPProbeToJson.deleteLater()
            self.VPTProbeToJson.terminate()
            self.VPTProbeToJson.deleteLater()
        elif process == "probeShow":
            self.VPProbe.deleteLater()
            self.VPTProbe.terminate()
            self.VPTProbe.deleteLater()
            self.showVideoInfoDialog(outjson)

        QApplication.processEvents()
        self.progressBarProcessor.setValue(0)
        return

    def QThreadError(self, processor, e, exception_string):
        """ Threads Errors"""
        qgsu.showUserAndLogMessage(QCoreApplication.translate(
            "QgsFmvPlayer", processor),
                                   'Failed!\n'.format(exception_string),
                                   level=QGis.Warning)

        self.QThreadFinished(processor, "Closing Processor")

        return

    def OpenQgsFmvMetadata(self):
        """ Open Metadata Dock """
        if self.metadataDlg is None:
            self.metadataDlg = QgsFmvMetadata(parent=self, player=self)
            self.addDockWidget(Qt.RightDockWidgetArea, self.metadataDlg)
            self.metadataDlg.show()
        else:
            self.metadataDlg.show()
        return

    def KillAllProcessors(self):
        """Kill All Processors"""
        """ Extract all frames Processors """
        try:
            if self.VPTExtractAllFrames.isRunning():
                ret = qgsu.CustomMessage(
                    QCoreApplication.translate(
                        "QgsFmvPlayer", "HEY...Active background process!"),
                    QCoreApplication.translate("QgsFmvPlayer",
                                               "Do you really want close?"))
                if ret == QMessageBox.Yes:
                    self.QThreadFinished("ExtractFramesProcessor",
                                         "Closing Extract Frames Processor")
                else:
                    return False
        except:
            None
        """ Bitrates Processors"""
        try:
            if self.VPTBitratePlot.isRunning():
                ret = qgsu.CustomMessage(
                    QCoreApplication.translate(
                        "QgsFmvPlayer", "HEY...Active background process!"),
                    QCoreApplication.translate("QgsFmvPlayer",
                                               "Do you really want close?"))
                if ret == QMessageBox.Yes:
                    self.QThreadFinished("CreatePlotsBitrate",
                                         "Closing Plot Bitrate")
                else:
                    return False
        except:
            None
        """ Converter Processors """
        try:
            if self.VPTConverter.isRunning():
                ret = qgsu.CustomMessage(
                    QCoreApplication.translate(
                        "QgsFmvPlayer", "HEY...Active background process!"),
                    QCoreApplication.translate("QgsFmvPlayer",
                                               "Do you really want close?"))
                if ret == QMessageBox.Yes:
                    self.QThreadFinished("convert", "Closing convert")
                else:
                    return False
        except:
            None
        """ probeToJson Processors """
        try:
            if self.VPTProbeToJson.isRunning():
                ret = qgsu.CustomMessage(
                    QCoreApplication.translate(
                        "QgsFmvPlayer", "HEY...Active background process!"),
                    QCoreApplication.translate("QgsFmvPlayer",
                                               "Do you really want close?"))
                if ret == QMessageBox.Yes:
                    self.QThreadFinished("probeToJson", "Closing Info to Json")
                else:
                    return False
        except:
            None
        """ probeShow Processors """
        try:
            if self.VPTProbe.isRunning():
                ret = qgsu.CustomMessage(
                    QCoreApplication.translate(
                        "QgsFmvPlayer", "HEY...Active background process!"),
                    QCoreApplication.translate("QgsFmvPlayer",
                                               "Do you really want close?"))
                if ret == QMessageBox.Yes:
                    self.QThreadFinished("probeShow",
                                         "Closing Show Video Info")
                else:
                    return False
        except:
            None
        return True

    def showVideoInfoDialog(self, outjson):
        """ Show Video Information Dialog """
        view = QTreeView()
        model = QJsonModel()
        view.setModel(model)
        model.loadJsonFromConsole(outjson)

        self.VideoInfoDialog = QDialog(self)
        self.VideoInfoDialog.setWindowTitle("Video Information : " +
                                            self.fileName)
        self.VideoInfoDialog.setWindowIcon(
            QIcon(":/imgFMV/images/video_information.png"))

        self.verticalLayout = QVBoxLayout(self.VideoInfoDialog)
        self.verticalLayout.addWidget(view)
        view.expandAll()
        view.header().setSectionResizeMode(QHeaderView.ResizeToContents)

        self.VideoInfoDialog.setWindowFlags(Qt.Window
                                            | Qt.WindowCloseButtonHint)
        self.VideoInfoDialog.setObjectName("VideoInfoDialog")
        self.VideoInfoDialog.resize(500, 400)
        self.VideoInfoDialog.show()

    def closeEvent(self, evt):
        """ Close Event """
        if self.KillAllProcessors() is False:
            evt.ignore()
            return

        self.player.stop()
        self.parent._PlayerDlg = None
        self.parent.ToggleActiveFromTitle()
        RemoveVideoLayers()
        RemoveGroupByName()

        # Restore Filters State
        self.videoWidget.RestoreFilters()
        # QApplication.processEvents()
        del self.player
Exemple #22
0
class QmyMainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)  #调用父类构造函数,创建窗体
        self.ui = Ui_MainWindow()  #创建UI对象
        self.ui.setupUi(self)  #构造UI界面

        self.player = QMediaPlayer(self)  #创建视频播放器
        self.player.setNotifyInterval(1000)  #信息更新周期, ms
        self.player.setVideoOutput(self.ui.videoWidget)  #视频显示组件

        self.ui.videoWidget.installEventFilter(self)  #事件过滤器

        self.__duration = ""
        self.__curPos = ""

        self.player.stateChanged.connect(self.do_stateChanged)
        self.player.positionChanged.connect(self.do_positionChanged)
        self.player.durationChanged.connect(self.do_durationChanged)

##  ==============自定义功能函数========================

##  ==============event处理函数==========================

    def closeEvent(self, event):  #窗体关闭时
        # 窗口关闭时不能自动停止播放,需手动停止
        if (self.player.state() == QMediaPlayer.PlayingState):
            self.player.stop()

    def eventFilter(self, watched, event):  ##事件过滤器
        if (watched != self.ui.videoWidget):
            return super().eventFilter(watched, event)

        #鼠标左键按下时,暂停或继续播放
        if event.type() == QEvent.MouseButtonPress:
            if event.button() == Qt.LeftButton:
                if self.player.state() == QMediaPlayer.PlayingState:
                    self.player.pause()
                else:
                    self.player.play()

        #全屏状态时,按ESC键退出全屏
        if event.type() == QEvent.KeyPress:
            if event.key() == Qt.Key_Escape:
                if self.ui.videoWidget.isFullScreen():
                    self.ui.videoWidget.setFullScreen(False)

        return super().eventFilter(watched, event)

##  ==========由connectSlotsByName()自动连接的槽函数============

    @pyqtSlot()  ##打开文件
    def on_btnOpen_clicked(self):
        ##      curPath=os.getcwd()  #获取系统当前目录
        curPath = QDir.currentPath()  #获取系统当前目录
        title = "选择视频文件"
        filt = "视频文件(*.wmv *.avi);;所有文件(*.*)"
        fileName, flt = QFileDialog.getOpenFileName(self, title, curPath, filt)
        if (fileName == ""):
            return

        fileInfo = QFileInfo(fileName)
        baseName = fileInfo.fileName()
        ##      baseName=os.path.basename(fileName)
        self.ui.LabCurMedia.setText(baseName)

        curPath = fileInfo.absolutePath()
        QDir.setCurrent(curPath)  #重设当前目录

        media = QMediaContent(QUrl.fromLocalFile(fileName))
        self.player.setMedia(media)  #设置播放文件
        self.player.play()

    @pyqtSlot()  ##播放
    def on_btnPlay_clicked(self):
        self.player.play()

    @pyqtSlot()  ##暂停
    def on_btnPause_clicked(self):
        self.player.pause()

    @pyqtSlot()  ##停止
    def on_btnStop_clicked(self):
        self.player.stop()

    @pyqtSlot()  ##全屏
    def on_btnFullScreen_clicked(self):
        self.ui.videoWidget.setFullScreen(True)

    @pyqtSlot()  ##静音按钮
    def on_btnSound_clicked(self):
        mute = self.player.isMuted()
        self.player.setMuted(not mute)
        if mute:
            self.ui.btnSound.setIcon(QIcon(":/icons/images/volumn.bmp"))
        else:
            self.ui.btnSound.setIcon(QIcon(":/icons/images/mute.bmp"))

    @pyqtSlot(int)  ##音量调节
    def on_sliderVolumn_valueChanged(self, value):
        self.player.setVolume(value)

    @pyqtSlot(int)  ##播放进度调节
    def on_sliderPosition_valueChanged(self, value):
        self.player.setPosition(value)

##  =============自定义槽函数===============================

    def do_stateChanged(self, state):  ##状态变化
        isPlaying = (state == QMediaPlayer.PlayingState)

        self.ui.btnPlay.setEnabled(not isPlaying)
        self.ui.btnPause.setEnabled(isPlaying)
        self.ui.btnStop.setEnabled(isPlaying)

    def do_durationChanged(self, duration):  ##文件长度变化
        self.ui.sliderPosition.setMaximum(duration)

        secs = duration / 1000  #秒
        mins = secs / 60  #分钟
        secs = secs % 60  #余数秒
        self.__duration = "%d:%d" % (mins, secs)
        self.ui.LabRatio.setText(self.__curPos + "/" + self.__duration)

    def do_positionChanged(self, position):  ##当前播放位置变化
        if (self.ui.sliderPosition.isSliderDown()):
            return  #如果正在拖动滑条,退出

        self.ui.sliderPosition.setSliderPosition(position)
        secs = position / 1000  #秒
        mins = secs / 60  #分钟
        secs = secs % 60  #余数秒
        self.__curPos = "%d:%d" % (mins, secs)
        self.ui.LabRatio.setText(self.__curPos + "/" + self.__duration)
Exemple #23
0
class ApplicationWindow(QtWidgets.QMainWindow):
    global wavFileName
    global fig,chartFig
    global duration, counterClick
    global colorName, text_
    global startAnnotation, endTimeToPlay

    # >> QtMultimedia Signals
    #----------------------
    play = pyqtSignal()
    pause = pyqtSignal()
    stop = pyqtSignal()

    def __init__(self):
        global playerStarted
        global wavFileName, fig, chartFig
        global playerStarted, durationFlag, duration
        global colorName, text_, counterClick
        global startAnnotation, endTimeToPlay

        QtWidgets.QMainWindow.__init__(self)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

        self.main_widget = QtWidgets.QWidget(self)
        playerStarted = False

        #DEFINE PLAYER-PLAYLIST   
        #----------------------
        self.source = QtCore.QUrl.fromLocalFile(os.path.abspath(wavFileName))
        self.content = QMediaContent(self.source)
        self.player = QMediaPlayer()
        self.playlist = QMediaPlaylist(self)
        self.playlist.addMedia(self.content)
        self.player.setPlaylist(self.playlist)

        # >> Define annotations and gantt chart 
        #---------------------- 
        self.wave = Waveform()
        fig = self.wave
        self.chart = Chart()
        chartFig = self.chart

        # >> Define player buttons 
        #---------------------- 
        playButton = QPushButton("Play")
        pauseButton = QPushButton("Pause")
        stopButton = QPushButton("Stop")

        # >> Define layouts 
        #---------------------- 
        waveLayout = QVBoxLayout()
        waveLayout.addWidget(self.wave)
        waveLayout.addWidget(self.chart)

        line = QFrame()
        line.setFrameShape(QFrame.VLine)
        line.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Expanding)
        waveLayout.addWidget(line)

        #Buttons layout
        buttonLayout = QVBoxLayout()
        buttonLayout.addWidget(playButton)
        buttonLayout.addWidget(pauseButton)
        buttonLayout.addWidget(stopButton)
        buttonLayout.setAlignment(Qt.AlignTop)


        # >> Specify final layout align 
        #----------------------
        layout = QHBoxLayout(self.main_widget)
        layout.addLayout(waveLayout)
        layout.addLayout(buttonLayout)
        
        # >> Define buttons connections 
        #---------------------- 
        playButton.clicked.connect(self.Play)
        pauseButton.clicked.connect(self.Pause)
        stopButton.clicked.connect(self.Stop)


        self.main_widget.setFocus()
        self.setCentralWidget(self.main_widget)


    # PLAYER BUTTON FUNCTIONS

    # >> Play audio (whole signal or segment) 
    #---------------------- 
    def Play(self):
        global playerStarted
        global durationFlag
        global duration, counterClick
        global startTimeToPlay, endTimeToPlay, first

        #GET CLICKS FROM WAVEFORM
        #---------------------- 
        #Initialize connection-position ONCE
        if not playerStarted:
            #10ms for changePosition -> Not Delaying
            self.player.positionChanged.connect(self.checkPositionToStop)
            self.player.setNotifyInterval(10)
            if durationFlag==0:
                playerStarted = True
                startTimeToPlay = 0
                self.start = startTimeToPlay
                self.end = duration*1000 - 10
                endTimeToPlay = self.end
                counterClick = 3
            elif durationFlag==1:
                playerStarted = True
                self.start = startTimeToPlay
                self.end = duration*1000 - 10
                endTimeToPlay = self.end
                counterClick = 3
            elif durationFlag==2:
                playerStarted = True
                self.start = startTimeToPlay
                self.end = endTimeToPlay
            self.player.setPosition(self.start)

        playFlag = True
        self.player.play()

    # >> Pause audio playing 
    #----------------------
    def Pause(self):
        #Not begging from self.start
        playerStarted = True
        self.player.setPosition(self.time_)
        self.player.pause()

    # >> Stop audio playing 
    #----------------------
    def Stop(self):
        self.player.stop()
        #Begin again segment
        self.start = startTimeToPlay
        self.player.setPosition(self.start)

    # >> Check ms in audio to stop play 
    #----------------------
    def checkPositionToStop(self):
        self.time_ = self.player.position()
        print self.time_
        if self.time_ >= self.end:
            self.Stop()
            self.player.setPosition(self.start)

    def fileQuit(self):
        self.close()

    def closeEvent(self, ce):
        self.fileQuit()
Exemple #24
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        # iniciando conexio con google
        self.translate_client = translate.Client()

        self.my_state = _STATE_PLAY

        # Controles principales para organizar la ventana.
        self.widget = QWidget(self)
        self.layout = QVBoxLayout()
        self.bottom_layout = QHBoxLayout()

        # status layout
        self.statusLayout = StatusLayout()

        # Sutitles layout
        self.subLayout = SubtitleLayout()

        # time stuff
        self.time_layout = QHBoxLayout()
        self.label_time = QLabel()
        self.label_mi = QLabel()
        self.label_end = QLabel()

        # inicializar subtitulos
        self.list_frames = sub.frames(MY_PATH + SUB_PATH)
        self.text_frame = self.list_frames.pop()

        # Control de reproducción de video de Qt.
        self.video_widget = QVideoWidget(self)
        self.media_player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.media_player.setMedia(
            QMediaContent(QUrl.fromLocalFile(MY_PATH + VIDEO_PATH)))
        self.media_player.setVideoOutput(self.video_widget)

        # Botones de reproducción y pausa.

        # Deslizadores para el volumen y transición del video.
        self.seek_slider = QSlider(Qt.Horizontal)
        self.volume_slider = QSlider(Qt.Horizontal)
        self.volume_slider.setRange(0, 100)
        self.volume_slider.setValue(self.media_player.volume())

        self.volume_slider.sliderMoved.connect(self.media_player.setVolume)

        # actualizar la posicion

        self.seek_slider.sliderMoved.connect(self.change_media_player)
        self.media_player.positionChanged.connect(self.change_seek_bar)

        self.media_player.durationChanged.connect(self.change_duration)

        # Acomodar controles en la pantalla.
        self.layout.addLayout(self.statusLayout)
        self.layout.addWidget(self.video_widget)
        self.layout.addLayout(self.bottom_layout)
        self.layout.addLayout(self.time_layout)
        self.layout.addWidget(self.seek_slider)

        # andir layout
        self.layout.addLayout(self.subLayout)
        #conections
        self.subLayout.prev_button.clicked.connect(self.click_prev)
        self.subLayout.next_button.clicked.connect(self.click_next)

        self.bottom_layout.addWidget(self.volume_slider)

        # time stuff
        self.time_layout.addWidget(self.label_time)
        self.time_layout.addWidget(self.label_mi)
        self.time_layout.addWidget(self.label_end)

        #self.set_sub_text(self.labels_sub2, self.frame.txt)
        self.subLayout.set_sub_text(
            self.subLayout.labelSub1,
            self.text_frame.f1.get_txt_conver_asteric())
        self.subLayout.set_sub_text(
            self.subLayout.labelSub2,
            self.text_frame.f2.get_txt_conver_asteric())

        # Conectar los eventos con sus correspondientes funciones.
        self.statusLayout.play_button.clicked.connect(self.play_clicked)
        self.statusLayout.stop_button.clicked.connect(self.stop_clicked)
        self.media_player.stateChanged.connect(self.state_changed)

        # conectando labels
        self.connect_labels()

        # Personalizar la ventana.
        self.setWindowTitle("Reproductor de video")
        self.resize(800, 600)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.bottom_layout.setContentsMargins(0, 0, 0, 0)
        self.widget.setLayout(self.layout)
        self.setCentralWidget(self.widget)

        # setear Timer
        self.timer = QTimer()
        self.timer.timeout.connect(self.timer_change_status)
        self.timer.start(_TIMER_TICK)

        # Reproducir el video.
        self.media_player.setNotifyInterval(_NOTIFY_INTERVAL)
        self.media_player.play()

    def connect_labels(self):
        for element in self.subLayout.labelSub1:
            element.clicked.connect(self.label_cliked)

        for element in self.subLayout.labelSub2:
            element.clicked.connect(self.label_cliked)

    def miles_minutes(self, value):
        s, ms = divmod(value, 1000)
        m, s = divmod(s, 60)
        h, m = divmod(m, 60)
        return h, m, s, ms

# modificar el valor del seek

    def change_media_player(self, value):
        self.media_player.setPosition(value)

    def timer_change_status(self):
        #print ("timer")
        if self.my_state == _STATE_PAUSE:
            self.media_player.pause()
            self.timer.stop()

    def change_seek_bar(self, value):
        if self.my_state == _STATE_PLAY:
            # detiene la ejecusion del video si el frame ya acabo
            if value > self.text_frame.end:
                self.my_state = _STATE_PAUSE

        #if (value > self.text_frame.end and self.my_state == _STATE_PLAY):
        #    print ("pasa")
        #    self.media_player.pause()
        #    self.my_state = _STATE_PAUSE

        #self.text_frame = self.list_frames.pop()
        #if self.text_frame != None:
        #    self.sub_text_lab1       = self.text_frame.txt1
        #    self.sub_text_lab2       = self.text_frame.txt2
        #else :
        #    self.sub_text_lab1       = ""
        #    self.sub_text_lab2       = ""
        #    self.text_frame.end = 0

        #print ("casa")

            (h, m, s, ms) = self.miles_minutes(value)
            time = "%d:%d:%d.%d" % (h, m, s, ms)
            self.label_time.setText(time)
            self.label_mi.setText(str(value))
            self.label_end.setText(str(self.text_frame.end))

            #self.label_sub1.setText(self.frame.txt)
            #self.set_sub_text(self.labels_sub1, self.sub_text_lab1)
            #self.set_sub_text(self.labels_sub2, self.sub_text_lab2)
            self.seek_slider.setValue(value)

        return True

# se modifico la duracion

    def change_duration(self, value):
        self.seek_slider.setRange(0, value)


# senales relacionadas a eventos

    def play_clicked(self):
        """
        Comenzar o resumir la reproducción.
        """
        if (self.media_player.state()
                in (QMediaPlayer.PausedState, QMediaPlayer.StoppedState)):
            self.media_player.play()
        else:
            self.media_player.pause()

    def click_prev(self):
        if self.my_state == _STATE_PLAY:
            self.media_player.pause()
        self.timer.stop()
        self.media_player.setPosition(self.text_frame.start)
        self.timer.start(_TIMER_TICK)
        self.media_player.play()
        self.my_state = _STATE_PLAY

    def click_next(self):
        if self.my_state == _STATE_PLAY:
            self.media_player.pause()
        self.timer.stop()
        self.text_frame = self.list_frames.pop()
        self.media_player.setPosition(self.text_frame.start)
        self.timer.start(_TIMER_TICK)
        self.media_player.play()
        self.my_state = _STATE_PLAY

        self.subLayout.set_sub_text(
            self.subLayout.labelSub1,
            self.text_frame.f1.get_txt_conver_asteric())
        self.subLayout.set_sub_text(
            self.subLayout.labelSub2,
            self.text_frame.f2.get_txt_conver_asteric())

    def stop_clicked(self):
        """
        Detener la reproducción.
        """
        self.media_player.stop()

    def state_changed(self, newstate):
        """
        Actualizar el texto de los botones de reproducción y pausa.
        """
        states = {
            QMediaPlayer.PausedState: "Resumir",
            QMediaPlayer.PlayingState: "Pausa",
            QMediaPlayer.StoppedState: "Reproducir"
        }
        self.statusLayout.play_button.setText(states[newstate])
        self.statusLayout.stop_button.setEnabled(
            newstate != QMediaPlayer.StoppedState)

    def eventFilter(self, obj, event):
        """
        Establecer o remover pantalla completa al obtener
        el evento MouseButtonDblClick.
        """
        if event.type() == QEvent.MouseButtonDblClick:
            obj.setFullScreen(not obj.isFullScreen())
        return False

    def keyPressEvent(self, event):
        if type(event) == QtGui.QKeyEvent:
            if event.key() == Qt.Key_Space:
                print("tecla space")
            else:
                #print (event.key())
                if self.text_frame.new_key(event.key()) == True:
                    #print("cambiar al siguiente Frame")
                    self.text_frame = self.list_frames.pop()
                    if self.my_state == _STATE_PLAY:
                        self.media_player.pause()
                    self.media_player.setPosition(self.text_frame.start)
                    self.timer.start(_TIMER_TICK)
                    self.media_player.play()
                    self.my_state = _STATE_PLAY

                self.subLayout.set_sub_text(
                    self.subLayout.labelSub1,
                    self.text_frame.f1.get_txt_conver_asteric())
                self.subLayout.set_sub_text(
                    self.subLayout.labelSub2,
                    self.text_frame.f2.get_txt_conver_asteric())

            event.accept()
        else:
            event.ignore()

    def label_cliked(self, word):
        #print(word)
        result = self.translate_client.translate(
            word, target_language=_TARGET_LANGUAGE)
        txt = "%s = %s" % (result['input'], result['translatedText'])
        print(txt)
class ApplicationWindow(QMainWindow):
    ## Accessing files in Processed Stuff ##
    QDir.setCurrent(QCoreApplication.applicationDirPath())
    processedVideoPath = QDir.currentPath() + "/ProcessedStuff/horses_1_predicted.mp4";
    textFilePath = QDir.currentPath() + "/ProcessedStuff/labels_example.txt";
    
    # Environmental Variable
    # os.environ['PYTHONPATH'] = "$(pwd)/detection:$(pwd)/detection/slim"
    # subprocess.check_call(['sqsub', '-np', sys.argv[1], 'application.py'],
    #                   env=dict(os.environ, SQSUB_VAR="visible in this subprocess"))

    ## Important variables ##
    videoLoaded = False
    fileReceived = False
    playing = False
    fileName = None
    labels = []
    nextLine = 0

    def __init__(self):
        # Create the Qt5 application backend
        super(ApplicationWindow, self).__init__()

        # Load in and display the UI
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        ## CONNECT EVENTS (like button presses) to functions ##
        self.ui.button_play_pause.clicked.connect(self.playPauseButtonClicked)
        self.ui.button_load.clicked.connect(self.loadButtonClicked)
        self.ui.button_track.clicked.connect(self.trackButtonClicked)

        # Configure the original video widget
        self.original_video_player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.original_video_player.durationChanged.connect(self.durationChanged)
        self.original_video_player.setVideoOutput(self.ui.original_video_widget)

        # Configure the processed video widget
        self.video_player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.video_player.setNotifyInterval(30)
        self.video_player.durationChanged.connect(self.durationChanged)
        self.video_player.positionChanged.connect(self.positionChanged)
        self.video_player.stateChanged.connect(self.stopped)
        self.video_player.setVideoOutput(self.ui.video_widget)

        # Configure the video slider
        self.ui.video_slider.sliderPressed.connect(self.sliderPressed)
        self.ui.video_slider.sliderReleased.connect(self.sliderReleased)

        # Update states
        self.updateStates()

    ##  CALLBACK FUNCTIONS ##
    def playPauseButtonClicked(self):
        if self.playing:
            print("Pause!")
            self.pause()
        else:
            print("Play!")
            self.play()

    def saveButtonClicked(self):
        print("Save!")

    def loadButtonClicked(self):
        print("Load!")
        # fileName, _ = QFileDialog.getOpenFileName(
        #     self,
        #     "Choose a video",
        #     QStandardPaths.writableLocation(QStandardPaths.DocumentsLocation),
        #     "Vide files (*mp4 *mp3)")
        # file = open("ProcessedStuff/labels_example.txt","w") 
        # file.write("labels = []\n")
        # file.close()
        fileName, _ = QFileDialog.getOpenFileName(self, "Open Files")

        if fileName != "":
            self.ui.file_info.setText(fileName)
            print("Video filepath: " + fileName)
            self.fileName = fileName
            self.fileReceived = True
            self.nextLine = 0
            self.updateStates()

    def trackButtonClicked(self):
        print("Track!")

        # Do processing and call following method when completed #
        detection.detection_code(self.fileName)
        self.processingFinished()

    def sliderReleased(self):
        newPosition = self.ui.video_slider.value()
        self.setPosition(newPosition)

    def sliderPressed(self):
        self.pause()

    def durationChanged(self):
        self.videoLoaded = True
        self.updateStates()
        self.ui.video_slider.setMaximum(self.video_player.duration())

    def positionChanged(self):
        if not self.playing:
            return
        position = self.video_player.position()
        # Update video progress bar
        self.ui.video_slider.setValue(position)
        # Update information label
        self.updateLabel(position)

    def stopped(self):
        if self.video_player.state() == QMediaPlayer.StoppedState:
            self.playing = False
            self.ui.label_info.setText("")

    ## Helper Functions ##
    def play(self):
        self.original_video_player.play()
        self.video_player.play()
        self.playing = True

    def pause(self):
        self.playing = False
        self.original_video_player.pause()
        self.video_player.pause()

    def setPosition(self, position):
        self.resetNextLine(position)
        self.updateLabel(position)
        self.original_video_player.setPosition(position)
        self.video_player.setPosition(position)
        self.play()

    def updateStates(self):
        self.ui.button_play_pause.setEnabled(self.videoLoaded)
        self.ui.video_slider.setEnabled(self.videoLoaded)
        self.ui.button_track.setEnabled(self.fileReceived and not self.playing)

    def processingFinished(self):
        print("Text file location: " + self.textFilePath)
        file = open(self.textFilePath, "r")
        self.labels = [line.split(',') for line in file]
        file.close()
        print(self.processedVideoPath)
        self.video_player.setMedia(QMediaContent(QUrl.fromLocalFile(self.processedVideoPath)))
        self.original_video_player.setMedia(QMediaContent(QUrl.fromLocalFile(self.fileName)))
        self.play()

    # When the user changes the slider the next line changes
    def resetNextLine(self, position):
        while (position < int(self.labels[self.nextLine][0])):
            self.nextLine -= 1
            if (self.nextLine <= 0):
                break
        if self.nextLine+1<=len(self.labels)-1:
            while (position >= int(self.labels[self.nextLine+1][0])):
                self.nextLine += 1
                if (self.nextLine >= len(self.labels)-1):
                    break

    def updateLabel(self, position):
        if position >= int(self.labels[self.nextLine][0]):
            label = self.labels[self.nextLine]
            messageStr = "Count: " + label[1]
            for i in range(2,len(label),2):
                messageStr += ("\n%s. %s %s" % (i-int(i/2),label[i],label[i+1]))
            self.ui.label_info.setText(messageStr)
            if self.nextLine < len(self.labels)-1:
                self.nextLine += 1
Exemple #26
0
class MyMainForm(QMainWindow, Ui_MainWindow):
    signal_close_popup = pyqtSignal()

    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        Ui_MainWindow.__init__(self)
        self.setupUi(self)

        self.setWindowTitle(' ')
        self.setWindowFlags(Qt.WindowMinimizeButtonHint
                            | Qt.WindowCloseButtonHint
                            | Qt.MSWindowsFixedSizeDialogHint)

        self.songs_data_manager = SongsDataManager()
        self.songs_data = self.songs_data_manager.read_songs_data()

        self.column_num = 2
        self.row_num = math.ceil(globals.style_num / self.column_num)

        self._generate_colors(globals.style_num)

        self.action = QAction('导入')
        self.main_menu.addAction(self.action)
        self.action.triggered.connect(self._import_files)

        self.styles_widget.load_styles_images()

        self.style_table_widget.setVisible(False)
        self.style_table_widget.setShowGrid(False)
        self.style_table_widget.setFrameShape(QFrame.NoFrame)
        self.style_table_widget.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch)
        self.style_table_widget.horizontalHeader().setVisible(False)
        self.style_table_widget.verticalHeader().setVisible(False)
        self.style_table_widget.verticalHeader().setSectionResizeMode(
            QHeaderView.Stretch)
        self.style_table_widget.setColumnCount(self.column_num)
        self.style_table_widget.setRowCount(self.row_num)

        self.table_items = []
        self.playlist_items = []
        self.selected_style_index = -1
        self.selected_song_name = ''
        self.location_root = os.getcwd()

        for i in range(self.row_num * self.column_num):
            row = math.floor(i / self.column_num)
            column = math.floor(i % self.column_num)
            if i < globals.style_num:
                table_item = QTableWidgetItem(globals.styles_chinese[i])
                table_item.setData(Qt.FontRole, QFont("", 20))
                table_item.setBackground(self.qcolors[i])
            else:
                table_item = QTableWidgetItem('')
            self.table_items.append(table_item)
            table_item.setTextAlignment(Qt.AlignCenter)
            self.style_table_widget.setItem(row, column, table_item)

        self.style_table_widget.cellClicked.connect(
            self._on_table_item_clicked)

        self.video_widget = QVideoWidget()
        self.player_layout.addWidget(self.video_widget)
        control_layout = QHBoxLayout()
        control_layout.setContentsMargins(0, 0, 0, 0)
        self.play_button = QPushButton()
        self.play_button.setEnabled(False)
        self.play_button.setIcon(self.style().standardIcon(
            QStyle.SP_MediaPlay))
        self.play_button.clicked.connect(self.play)
        control_layout.addWidget(self.play_button)
        self.position_slider = QSlider(Qt.Horizontal)
        self.position_slider.setRange(0, 0)
        self.position_slider.sliderMoved.connect(self.set_position)
        control_layout.addWidget(self.position_slider)
        self.player_layout.addLayout(control_layout)

        self.back_btn.clicked.connect(self._on_back_to_style_view)
        self.delete_btn.clicked.connect(self._on_delete_item)

        self.playlist.itemClicked.connect(self._on_playlist_item_clicked)
        self.playlist.itemDoubleClicked.connect(
            self._on_playlist_item_double_clicked)

        self.playlist_widget.setVisible(False)

        self.media_player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.notify_interval = 100
        self.media_player.setVideoOutput(self.video_widget)
        self.media_player.setNotifyInterval(self.notify_interval)
        self.media_player.stateChanged.connect(self.media_state_changed)
        self.media_player.positionChanged.connect(self.play_time_changed)
        self.media_player.durationChanged.connect(self.duration_changed)
        self.media_player.error.connect(self.handle_error)

        self.styles_widget.signal_style_clicked.connect(self.on_style_selected)

        self.detected_result_str = ''
        with open('music_model.pkl', 'rb') as fp:
            self.model = pickle.load(fp)

    def closeEvent(self, event):
        self.media_player.pause()
        self.wave_widget.stop_audio()

    def _change_to_playlist_view(self, style_index):
        self.styles_widget.setVisible(False)
        self.playlist_widget.setVisible(True)
        self.selected_style_index = style_index
        self._refresh_playlist()

    def _refresh_playlist(self):
        files = self.songs_data.get(globals.styles[self.selected_style_index],
                                    [])
        self.playlist_items = []
        self.playlist.clear()
        flip_count = False
        for file in files:
            flip_count = False if flip_count else True
            list_item = QListWidgetItem(os.path.basename(file))
            setattr(list_item, 'full_file_name', file)
            list_item.setBackground(Qt.lightGray if flip_count else Qt.white)
            self.playlist_items.append(list_item)
            self.playlist.addItem(list_item)

    def _on_back_to_style_view(self):
        self.styles_widget.setVisible(True)
        self.playlist_widget.setVisible(False)
        self.selected_song_name = ''
        self.delete_btn.setEnabled(False)

    def _get_cur_style_dir(self):
        return os.path.join(self.location_root, 'playlists',
                            globals.styles[self.selected_style_index])

    @staticmethod
    def _pop_message_box(alert_text):
        result_window = MyPopup(has_close=True)
        result_window.label.setText(alert_text)
        result_window.exec()

    def on_style_selected(self, style_selected):
        self._change_to_playlist_view(globals.styles.index(style_selected))

    def _print_media_state(self):
        if self.media_player.state() == QMediaPlayer.PlayingState:
            print('playing state')
        elif self.media_player.state() == QMediaPlayer.PausedState:
            print('paused state')
        elif self.media_player.state() == QMediaPlayer.StoppedState:
            print('stopped state')

    def _on_table_item_clicked(self, row, col):
        style_index = row * self.column_num + col
        if style_index >= globals.style_num:
            return
        self._change_to_playlist_view(style_index)

    def _on_playlist_item_clicked(self, item):
        self.selected_song_name = getattr(item, 'full_file_name')
        self.delete_btn.setEnabled(True)

    def _on_playlist_item_double_clicked(self, item):
        if self.selected_style_index == -1:
            return
        filename = getattr(item, 'full_file_name')
        if os.path.isfile(filename):
            self.current_song_label.setText(os.path.basename(filename))
            self.media_player.setMedia(
                QMediaContent(QUrl.fromLocalFile(filename)))
            self.wave_widget.load_file(filename)
            self.play()
        else:
            self._pop_message_box('找不到文件:%s' % filename)

    def _on_delete_item(self):
        if not self.selected_song_name:
            return
        if self.selected_style_index == -1:
            return
        self.songs_data[globals.styles[self.selected_style_index]].pop(
            self.selected_song_name, None)
        self.songs_data_manager.write_songs_data(self.songs_data)
        self._refresh_playlist()

    def _import_files(self):
        file_dialog = QFileDialog()
        file_dialog.setFileMode(QFileDialog.ExistingFiles)
        names = file_dialog.getOpenFileNames(None, 'open wav file', '.',
                                             'AUDIO(*.wav *.mp3)')
        if not names[0]:
            return
        if not isinstance(names[0], list):
            audio_names = [names[0]]
        else:
            audio_names = names[0]
        self.processing_files_thread = ProcessingFilesThread(main_widget=self,
                                                             names=audio_names)
        self.processing_files_thread.start()
        main_window_geometry = self.geometry()
        self.progress_window = MyPopup(main_widget=self)
        self.progress_window.exec()
        self.processing_files_thread.wait()
        self._pop_message_box(self.detected_result_str)
        self._refresh_playlist()

    def play(self):
        self.play_button.setEnabled(True)
        if self.media_player.state() == QMediaPlayer.PlayingState:
            self.media_player.pause()
            self.wave_widget.pause_audio()
        else:
            self.media_player.play()
            self.wave_widget.start_audio()

    def media_state_changed(self, state):
        if state == QMediaPlayer.PlayingState:
            self.play_button.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPause))
        else:
            self.play_button.setIcon(self.style().standardIcon(
                QStyle.SP_MediaPlay))

        if state == QMediaPlayer.StoppedState:
            self.wave_widget.stop_audio()

    def play_time_changed(self, play_time):
        self.wave_widget.set_cur_play_time(play_time)
        self.position_slider.setValue(play_time)

    def duration_changed(self, duration):
        self.position_slider.setRange(0, duration)

    def set_position(self, position):
        self.media_player.setPosition(position)

    def handle_error(self):
        self.play_button.setEnabled(False)

    def _generate_colors(self, num):
        colors = []
        for r_value in range(0, 256, 20):
            for g_value in range(0, 256, 20):
                for b_value in range(0, 256, 20):
                    colors.append((r_value, g_value, b_value))

        colors_choosen = random.sample(colors, num)
        self.qcolors = []
        for color in colors_choosen:
            self.qcolors.append(QColor(color[0], color[1], color[2], 127))
Exemple #27
0
class VideoWindow(QWidget):
    sigTimeChanged = pyqtSignal(object)

    def __init__(self, project=None, parent=None):
        super(VideoWindow, self).__init__(parent)
        self.project = project
        self.setWindowTitle("Video")
        self.mediaPlayer = QMediaPlayer()  #None, QMediaPlayer.VideoSurface)
        self.last_position = 0
        self.position_on_new_file = 0
        self.duration = -1
        self.waiting_for_file = False
        self.media_state_before_file_transition = self.mediaPlayer.state()
        self.video_time_offset = 0.0

        self.play_icon = QIcon(play_icon_file)
        self.clock_icon = QIcon(clock_icon_file)
        self.pause_icon = QIcon(pause_icon_file)

        videoWidget = QVideoWidget()
        self.videoWidget = videoWidget
        self.playButton = QPushButton()
        self.playButton.setEnabled(False)
        self.playButton.setIcon(self.play_icon)
        self.playButton.clicked.connect(self.play)

        self.timeOffsetButton = QPushButton()
        self.timeOffsetButton.setIcon(self.clock_icon)
        self.timeOffsetButton.clicked.connect(self.setTimeOffset)

        self.positionSlider = QSlider(Qt.Horizontal)
        self.positionSlider.setRange(0, 0)
        self.positionSlider.sliderMoved.connect(self.setPosition)

        self.errorLabel = QLabel()
        self.errorLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Maximum)

        # Create layouts to place inside widget
        controlLayout = QHBoxLayout()
        controlLayout.setContentsMargins(0, 0, 0, 0)
        controlLayout.addWidget(self.timeOffsetButton)
        controlLayout.addWidget(self.playButton)
        controlLayout.addWidget(self.positionSlider)

        layout = QVBoxLayout()
        layout.addWidget(videoWidget)
        layout.addLayout(controlLayout)
        layout.addWidget(self.errorLabel)  # Hide error Label

        # Set widget to contain window contents
        self.setLayout(layout)

        self.mediaPlayer.setVideoOutput(videoWidget)
        self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
        self.mediaPlayer.positionChanged.connect(self.positionChanged)
        self.mediaPlayer.durationChanged.connect(self.durationChanged)
        self.mediaPlayer.mediaStatusChanged.connect(self.mediaStatusChanged)
        self.mediaPlayer.error.connect(self.handleError)
        self.mediaPlayer.setNotifyInterval(40)  # 25 fps

        if self.project is None:
            self.current_time_range = [0, 0]
            self.current_file = ''
            # self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(self.current_file)))
            # self.playButton.setEnabled(True)
            # self.mediaPlayer.play()
        elif self.project.current_animal.video_files:
            self.current_file = self.project.current_animal.video_files[0]
            self.current_time_range = [
                self.project.current_animal.video_init_time[0],
                self.project.current_animal.video_init_time[0] +
                self.project.current_animal.video_duration[0]
            ]
            self.mediaPlayer.setMedia(
                QMediaContent(QUrl.fromLocalFile(self.current_file)))
            self.playButton.setEnabled(True)
        else:
            self.current_file = ''
            self.current_time_range = [0, 0]

    def setTimeOffset(self):
        offset, okpressed = QInputDialog.getDouble(
            self,
            'Video time offset',
            'Offset video time position (seconds)',
            value=self.video_time_offset)
        if okpressed:
            self.video_time_offset = offset
            current_position = self.current_time_range[
                0] + self.last_position / 1000
            self.setGlobalPosition(0)
            self.setGlobalPosition(current_position)

    def openFile(self):
        fileName, _ = QFileDialog.getOpenFileName(self, "Open Movie",
                                                  QDir.homePath())

        if fileName != '':
            self.mediaPlayer.setMedia(
                QMediaContent(QUrl.fromLocalFile(fileName)))
            self.playButton.setEnabled(True)

    def exitCall(self):
        sys.exit(app.exec_())

    def play(self):
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()
        else:
            self.mediaPlayer.play()

    def mediaStateChanged(self, state):
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.playButton.setIcon(self.pause_icon)
        else:
            self.playButton.setIcon(self.play_icon)

    def positionChanged(self, position):
        # Connected to video player
        # print('positionChanged',position,self.last_position,self.waiting_for_file,self.duration,self.current_time_range)
        # if self.duration == -1:
        #     print('positionChanged: no file - duration ==-1')
        #     return
        # if self.waiting_for_file:
        #     print('positionChanged: Waiting to load file')
        #     return
        # if position == 0:
        #     print('positionChanged: avoiding setting positions to 0')
        #     return
        if position == 0 or self.waiting_for_file or self.duration == -1 or position == self.last_position:
            # avoid position changes on file transitions or repeated signals on same position
            return
        if position < self.duration - 40:  # avoid time changes when switching files
            self.last_position = position
            self.positionSlider.setValue(position)
            self.sigTimeChanged.emit(position / 1000 +
                                     self.current_time_range[0])
        else:  # position is at the end of file - try to switch to next file
            pos = self.current_time_range[1] + .04
            print('Trying to jump to next file', self.current_time_range[1],
                  self.duration, pos)
            self.setGlobalPosition(pos)

    def durationChanged(self, duration):
        # print('duration changed',duration)
        self.duration = duration
        self.positionSlider.setRange(0, duration)
        self.mediaPlayer.setPosition(
            self.position_on_new_file
        )  # if duration changes avoid the position going back to 0

    def setPosition(self, position):
        # connected to slider
        # print('setPosition',position)
        self.mediaPlayer.setPosition(
            position)  #  milliseconds since the beginning of the media

    def setGlobalPosition(self, pos):
        # Connected to project main model sigTimeChanged
        # open the right media
        if self.current_time_range[0] <= pos <= self.current_time_range[
                1]:  # correct file opened
            position = int((pos - self.current_time_range[0]) * 1000)
            if self.mediaPlayer.state() == QMediaPlayer.PlayingState and abs(
                    position - self.last_position) < 200:
                # skip position setting by signal of main model to ensure smooth video plaback
                return
            # go to correct relative position
            self.mediaPlayer.setPosition(position)  # UNIX time
            return
        else:
            for i, file in enumerate(self.project.current_animal.video_files
                                     ):  # search for file to open
                arange = [
                    self.project.current_animal.video_init_time[i] +
                    self.video_time_offset,
                    self.project.current_animal.video_init_time[i] +
                    self.project.current_animal.video_duration[i] +
                    self.video_time_offset
                ]
                if (arange[0] <= pos <= arange[1]):
                    print('Changing video file: ', file)
                    self.current_file = file
                    self.errorLabel.setText("File: " + self.current_file)
                    self.current_time_range = arange
                    self.waiting_for_file = True
                    self.media_state_before_file_transition = self.mediaPlayer.state(
                    )
                    self.mediaPlayer.stop()
                    position = (pos - self.current_time_range[0]) * 1000
                    self.position_on_new_file = int(position)
                    # print('Changing position_on_new_file: ', self.position_on_new_file,pos)
                    self.mediaPlayer.setMedia(
                        QMediaContent(QUrl.fromLocalFile(file)))
                    self.playButton.setEnabled(True)
                    # self.duration = (arange[1]-arange[0])*1000
                    return
        print('no video file found for current position')
        self.errorLabel.setText("No video file found for current position")
        self.mediaPlayer.stop()
        self.mediaPlayer.setMedia(QMediaContent())
        self.current_file = ''
        self.current_time_range = [0, 0]
        # self.duration = 0
        self.playButton.setEnabled(False)
        self.positionSlider.setRange(0, 0)
        self.positionSlider.setValue(0)

    def mediaStatusChanged(self, status):
        if self.waiting_for_file:
            if self.mediaPlayer.mediaStatus() == QMediaPlayer.LoadedMedia:
                self.waiting_for_file = False
                # print('finished loading file')
                # self.mediaPlayer.stop()
                self.mediaPlayer.setPosition(self.position_on_new_file)
                self.mediaPlayer.play()
                time.sleep(.05)
                self.mediaPlayer.pause()
                if self.media_state_before_file_transition == QMediaPlayer.PlayingState:
                    self.mediaPlayer.play()
                # print('finished setting position on new file')

    def handleError(self):
        self.playButton.setEnabled(False)
        self.errorLabel.setText("Error: " + self.mediaPlayer.errorString())
        print("Video - Error: " + self.mediaPlayer.errorString())
Exemple #28
0
class jaabaGUI(QMainWindow):
    """ controller for the blob labeling GUI"""
    def __init__(self, parent=None):
        self.debugMode = True
        self.debugVideoPath = '/Users/071cht/Desktop/Lab/jaabagui/testt.mjpeg.avi'

        QMainWindow.__init__(self, parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.installEventFilter(self)
        self.setFocusPolicy(Qt.StrongFocus)
        #add new slider
        # self.positionSlider=QSlider(Qt.Horizontal)
        # self.positionSlider.setGeometry (800,800,100,30)
        # self.positionSlider.setRange(0, 0)
        # self.positionSlider.sliderMoved.connect(self.setPosition)

        #setup Video
        #video player
        self.mediaPlayer1 = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.mediaPlayer2 = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.mediaPlayer2.setNotifyInterval(10)
        #self.mediaPlayer.metaDataChanged.connect(self.metaDataChanged)
        self.mediaPlayer1.durationChanged.connect(self.durationChanged)
        self.mediaPlayer1.positionChanged.connect(self.positionChanged)
        self.mediaPlayer2.positionChanged.connect(self.positionChanged)
        #self.mediaPlayer2.positionChanged.connect(self.paintEvent)

        #visualizetion
        self.scene = QGraphicsScene()
        self.ui.graphicsView.setScene(self.scene)
        #self.scene.setBackgroundBrush(Qt.black)
        self.videoItem1 = QGraphicsVideoItem()
        self.videoItem2 = Video()
        self.scene.addItem(self.videoItem1)
        self.scene.addItem(self.videoItem2)
        self.mediaPlayer1.setVideoOutput(self.videoItem1)
        self.mediaPlayer2.setVideoOutput(self.videoItem2)

        #slider bar
        self.ui.horizontalSlider.setRange(0, 0)
        self.ui.horizontalSlider.sliderMoved.connect(self.setPosition)
        # self.ui.horizontalSlider.sliderPressed.connect(self.sliderPressed)

        #draw on video
        self.flyCanvas = TargetView()
        self.scene.addItem(self.flyCanvas)
        #give reference to target view
        self.flyCanvas.setWindowReference(self)

        #lineEdit signals:
        self.ui.lineEdit.returnPressed.connect(self.lineEditChanged)

        #callbacks
        self.ui.actionQuit.triggered.connect(self.quit)
        self.ui.actionLoad_Project.triggered.connect(self.loadVideo)
        self.ui.actionImport_Labels.triggered.connect(self.loadLabels)
        #self.ui.buttonPlay.clicked[bool].connect(self.setToggleText)
        self.ui.buttonPlay.clicked.connect(self.play)
        self.ui.actionSave.triggered.connect(self.saveLabels)
        ## print self.ui.graphicsView.sizeHint()

        #behavior Button
        self.ui.buttonBehavior.clicked.connect(self.behaviorButtonClick)
        self.ui.buttonNone.clicked.connect(self.noneButtonClick)

        #initialization
        self.loaded = False
        self.videoFilename = None
        self.frame_count = None
        self.width = None
        self.height = None
        self.frame_trans = None
        self.previous_frame = 0
        self.current_frame = 0
        self.behaviorButtonStart = False
        self.noneButtonStart = False
        self.currentFly = 1

        #initialize flyInfo
        #self.setCurrentFly(self.currentFly)

        # register flyid changed callback
        self.flyCanvas.onCurrentFlyIdChanged(self.currentFlyIdChangedCallback)
        self.flyCanvas.setCurrentFlyId(self.currentFly)

        # when double click on video, change fly id in target view
        self.videoItem2.onDoubleClick(self.flyCanvas.setCurrentFlyIdByXY)

        ########################
        # DEBUG PART HERE!!!!! #
        ########################
        if (self.debugMode):
            self.debugLoadVideo()

    # add label UI related when load video
    def showEvent(self, evt):
        super(jaabaGUI, self).showEvent(evt)
        ##### HERE THE WINDOW IS LOADED!!!!!!!!
        # self.loadLabelUI()

    def loadLabelUI(self):
        #labels
        self.labelScene = QGraphicsScene()

        self.ui.graphLabels.setScene(self.labelScene)
        # the size is only accurate after the window fully displayed
        labelUIWidth = self.ui.graphLabels.width()
        labelUIHeight = self.ui.graphLabels.height() - 1

        self.labelScene.setSceneRect(0, 0, labelUIWidth, labelUIHeight)

        self.labelUI = LabelUI()
        # visiableWidth = 850
        # height = 30
        # visiableFrameNum = 850

        self.labelUI.setWidthPerFrame(850.0 / 850.0)
        # print '850/500',850.0/850.0b
        # print 'length_perframe is ', self.labelUI.widthPerFrame
        # 850 is the original length of graphLabel
        total_length = self.labelUI.widthPerFrame * self.frame_count
        self.labelUI.setVisiableSize(total_length, 30)

        # set start position
        self.labelUI.setPos(labelUIWidth / 2, 0)

        print 'frame_count is ', self.frame_count
        print 'total length is', total_length

        self.labelScene.addItem(self.labelUI)

        # middle line ui
        self.labelUIMiddleLine = LabelUIMiddleLine()
        self.labelScene.addItem(self.labelUIMiddleLine)
        self.labelUIMiddleLine.setPos(labelUIWidth / 2, 0)

        # self.labelUI.setPos(QPointF(-100,0))
        self.writeLog('Label UI loaded')

    def eventFilter(self, obj, event):

        if (event.type() == PyQt5.QtCore.QEvent.KeyPress):
            # http://qt-project.org/doc/qt-4.8/qt.html#Key-enum
            key = event.key()

            if (key == Qt.Key_Up):
                curr_frame = int(float(self.ui.lineEdit.text()))
                curr_frame = curr_frame - 30
                media_position = int(round(curr_frame * self.frame_trans))

                # print curr_frame, media_position
                self.mediaPlayer1.setPosition(media_position)
                self.mediaPlayer2.setPosition(media_position)

                # print 'down -30'
            elif (key == Qt.Key_Right):
                curr_frame = int(float(self.ui.lineEdit.text()))
                # print 'right +1'
                # print curr_frame
                curr_frame = curr_frame + 1
                media_position = int(round(curr_frame * self.frame_trans))
                # print 'curr_frame',curr_frame
                # print 'frame_trans',self.frame_trans
                # print ' curr_frame*self.frame_trans',curr_frame*self.frame_trans
                # print 'media_position',media_position

                # print curr_frame, media_position
                self.mediaPlayer1.setPosition(media_position)
                self.mediaPlayer2.setPosition(media_position)
                # self.mediaPlayerPositionChanged(media_position)
            elif (key == Qt.Key_Left):
                curr_frame = int(float(self.ui.lineEdit.text()))
                curr_frame = curr_frame - 1
                media_position = int(round(curr_frame * self.frame_trans))
                self.mediaPlayer1.setPosition(media_position)
                self.mediaPlayer2.setPosition(media_position)
                # print 'left -1'
            elif (key == Qt.Key_Down):
                curr_frame = int(float(self.ui.lineEdit.text()))
                curr_frame = curr_frame + 30
                media_position = int(round(curr_frame * self.frame_trans))
                self.mediaPlayer1.setPosition(media_position)
                self.mediaPlayer2.setPosition(media_position)
                # print 'up +30'
            return True

        return False

    # ###actions starts from here###
    def quit(self):
        QApplication.quit()

    def loadVideo(self):

        # print QMediaPlayer.supportedMimeTypes()

        self.writeLog("Loading video...")

        self.videoFilename = QFileDialog.getOpenFileName(
            self, 'Open File', '.')[0]
        if not self.videoFilename:
            self.writeLog("User cancelled - no video loaded")
            return
        else:
            cap = cv2.VideoCapture(self.videoFilename)
            self.frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
            self.width = cap.get(3)
            self.height = cap.get(4)

            self.mediaPlayer2.setMedia(
                QMediaContent(QUrl.fromLocalFile(self.videoFilename)))
            self.mediaPlayer1.setMedia(
                QMediaContent(QUrl.fromLocalFile(self.videoFilename)))
            self.ui.buttonPlay.setEnabled(True)
        # self.mediaPlayer2.setVideoOutput(self.videoItem2)
        # self.mediaPlayer1.setVideoOutput(self.videoItem1)
        # size= self.videoItem2.nativeSize()
        # # print size
        ## print self.mediaPlayer.duration()

        ## print self.mediaPlayer.metaData()
        self.writeLog("Video loaded!")

        # init label related ui
        self.loadLabelUI()

    def debugLoadVideo(self):

        self.videoFilename = self.debugVideoPath

        cap = cv2.VideoCapture(self.videoFilename)
        self.frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
        self.width = cap.get(3)
        self.height = cap.get(4)

        self.mediaPlayer2.setMedia(
            QMediaContent(QUrl.fromLocalFile(self.videoFilename)))
        self.mediaPlayer1.setMedia(
            QMediaContent(QUrl.fromLocalFile(self.videoFilename)))
        self.ui.buttonPlay.setEnabled(True)
        self.writeLog("Video loaded!")

        QTimer.singleShot(1000, self.loadLabelUI)

    def play(self):

        self.videoItem1.setAspectRatioMode(0)
        self.videoItem2.setAspectRatioMode(0)
        self.scene.setSceneRect(0, 0, self.ui.graphicsView.width(),
                                self.ui.graphicsView.height())
        self.videoItem1.setSize(
            QSizeF(self.ui.graphicsView.width() / 2,
                   self.ui.graphicsView.height()))
        self.videoItem2.setSize(
            QSizeF(self.ui.graphicsView.width() / 2,
                   self.ui.graphicsView.height()))
        self.videoItem1.setPos(QPointF(0, 0))
        self.videoItem2.setPos(QPointF(self.ui.graphicsView.width() / 2, 0))
        self.flyCanvas.setPos(QPointF(self.ui.graphicsView.width() / 2, 0))

        # custom function setXYScale
        self.videoItem2.setXYScale(self.width, self.height,
                                   self.ui.graphicsView.width() / 2,
                                   self.ui.graphicsView.height())
        self.flyCanvas.setXYScale(self.width, self.height,
                                  self.ui.graphicsView.width() / 2,
                                  self.ui.graphicsView.height())

        if self.mediaPlayer1.state() == QMediaPlayer.PlayingState:
            self.ui.buttonPlay.setIcon(self.ui.style().standardIcon(
                PyQt5.QtWidgets.QStyle.SP_MediaPlay))
            self.ui.buttonPlay.setText("Play")
            self.mediaPlayer1.pause()
            self.writeLog("Video paused")
        else:
            self.ui.buttonPlay.setIcon(self.ui.style().standardIcon(
                PyQt5.QtWidgets.QStyle.SP_MediaPause))
            self.ui.buttonPlay.setText("Stop")

            self.mediaPlayer1.play()
            self.writeLog("Playing video")

        if self.mediaPlayer2.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer2.pause()
        else:
            self.mediaPlayer2.play()

    def loadLabels(self):

        self.writeLog("Loading labels from file...")
        self.labelFilename = QFileDialog.getOpenFileName(
            self, 'Open File', '.')[0]
        self.labelUI.labelData = pickle.load(open(self.labelFilename, "rb"))
        self.writeLog("Label loaded from file:" + self.labelFilename)

    def saveLabels(self):
        # Now it can only save to current file. Will add an poput window to choose path later
        pickle.dump(self.labelUI.labelData, open("newLabels.p", "wb"))

    def setPosition(self, position):
        self.mediaPlayer1.setPosition(position)
        self.mediaPlayer2.setPosition(position)

    # when position of media changed, set slider and text box accordingly.
    def positionChanged(self, position):
        #test change labelui position
        # self.labelUI.startLabel();
        # self.labelUI.update()
        previous_frame = self.previous_frame
        curr_frame = int(round(position / self.frame_trans))
        self.current_frame = curr_frame
        frame_change = previous_frame - curr_frame
        move_width = frame_change * self.labelUI.widthPerFrame
        self.previous_frame = curr_frame

        self.labelUI.moveBy(move_width, 0)

        self.labelUI.setCurrentFrame(curr_frame)
        # enforce labelUI paint once
        self.labelUI.update()

        # self.labelUI.setPos(self.labelUI.mapToParent(1,0));
        # self.labelUI.update()

        # # print 'triggered position'
        # # print position
        # # print 'cur position'
        # # print self.mediaPlayer2.position()
        self.updateLineEdit(position)
        self.updateSliderAndGraph(position)

    #  self.ui.horizontalSlider.setValue(position)

    #  if isinstance(self.frame_trans,float):
    #   # # print type(position),position
    #   # # print type(self.frame_trans),self.frame_trans
    #   # # print position/self.frame_trans
    # self.ui.lineEdit.setText(str(int(round(position/self.frame_trans))))
    # self.flyCanvas.getFrame(int(round(position/self.frame_trans)))
    # self.flyCanvas.isManualCalled = True;
    # self.flyCanvas.update()

    #  self.writeLog(str(position))
    # # self.updateMediaControlUI(position)
    # # self.flyCanvas.update()

    def updateSliderAndGraph(self, position):
        self.ui.horizontalSlider.setValue(position)
        if isinstance(self.frame_trans, float):
            self.flyCanvas.getFrame(int(round(position / self.frame_trans)))
            self.flyCanvas.isManualCalled = True
            self.flyCanvas.update()

    #self.writeLog(str(position))
    def updateLineEdit(self, position):
        # # print self.width
        # # print self.height
        if isinstance(self.frame_trans, float):
            # # print type(position),position
            # # print type(self.frame_trans),self.frame_trans
            # # print position/self.frame_trans
            self.ui.lineEdit.setText(
                str(int(round(position / self.frame_trans))))

    def durationChanged(self, duration):
        self.ui.horizontalSlider.setRange(0, duration)
        self.frame_trans = self.mediaPlayer1.duration() / self.frame_count
        ## print self.frame_trans

#def eventFilter(self,source,event):
#if (event.type()==PyQt5.QtCore.QEvent.MousePress and source is self.videoItem2):
# 	pos=event.pos()
# 	# print('mouse position: (%d,%d)' % (pos.x(),pos.y()))
#    return PyQt5.QtGui.QWidget.eventFilter(self, source, event)

    def writeLog(self, text):
        self.ui.log.setText(text)

    # def eventFilter (self.ui.lineEdit,event):
    #     if event.type()==PyQt5.QtCore.QEvent

    def lineEditChanged(self):
        #set position of media
        curr_frame = int(float(self.ui.lineEdit.text()))
        media_position = int(round(curr_frame * self.frame_trans))
        self.mediaPlayer1.setPosition(media_position)
        self.mediaPlayer2.setPosition(media_position)
        # print 'setPosition'
        # print media_position
        # print 'after set'
        # print self.mediaPlayer2.position()
        # self.updateSliderAndGraph(media_position)

    def behaviorButtonClick(self):
        # flip flag
        self.behaviorButtonStart = not self.behaviorButtonStart

        # check click to start or stop
        if (self.behaviorButtonStart):
            # start labeling
            self.labelUI.startLabel(self.ui.comboBox.currentIndex(), '',
                                    self.current_frame)
            self.writeLog('start labeling')

        else:
            # stop lableing
            self.labelUI.stopLabel()
            self.writeLog('stop labeling')

    def noneButtonClick(self):
        # flip flag
        self.noneButtonStart = not self.noneButtonStart

        # check click to start or stop
        if (self.noneButtonStart):
            # start labeling
            self.labelUI.startLabel(self.ui.comboBox.currentIndex(), '_none',
                                    self.current_frame)
            self.writeLog('start labeling')
        else:
            # stop lableing
            self.labelUI.stopLabel()
            self.writeLog('stop labeling')

    # set CurrentFly when fly changed!
    def setCurrentFly(self, fly):
        self.currentFly = fly
        self.ui.flyInfo.setPlainText('FlyID:' + str(self.currentFly))
        self.flyCanvas.currentFly = fly

    def currentFlyIdChangedCallback(self, fly):
        print 'callback!!!!!'
        self.currentFly = fly
        self.ui.flyInfo.setPlainText('FlyID:' + str(self.currentFly))