Example #1
0
class FrameSelectWidget(QWidget):
    frameSelectionChanged = pyqtSignal(int, QTime)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setMaximumWidth(960)

        layout = QVBoxLayout()
        layout.setContentsMargins(4, 4, 4, 4)
        layout.setSpacing(4)
        self.setLayout(layout)

        self.imageView = QImageView(self)
        layout.addWidget(self.imageView)

        self.slider = Slider(self)
        self.slider.setOrientation(Qt.Horizontal)
        self.slider.valueChanged.connect(self.handleSliderChange)
        self.slider.setTickInterval(1)
        layout.addWidget(self.slider)

        hlayout = QHBoxLayout()
        layout.addLayout(hlayout)

        self.prevLabel = QLabel(self)
        self.currentIndex = QSpinBox(self)
        self.currentIndex.valueChanged.connect(self.handleIndexChange)
        self.currentTime = QTimeEdit(self)
        self.currentTime.setDisplayFormat("H:mm:ss.zzz")
        self.currentTime.timeChanged.connect(self.handleTimeChange)
        self.nextLabel = QLabel(self)

        hlayout.addWidget(self.prevLabel)
        hlayout.addStretch()
        hlayout.addWidget(QLabel("Frame index:", self))
        hlayout.addWidget(self.currentIndex)
        hlayout.addWidget(QLabel("Timestamp:", self))
        hlayout.addWidget(self.currentTime)
        hlayout.addStretch()
        hlayout.addWidget(self.nextLabel)

        hlayout = QHBoxLayout()
        hlayout.setContentsMargins(0, 0, 0, 0)
        hlayout.setSpacing(4)
        layout.addLayout(hlayout)

        self.okayBtn = QPushButton("&OK", self)
        self.cancelBtn = QPushButton("&Cancel", self)

        hlayout.addStretch()
        hlayout.addWidget(self.okayBtn)
        hlayout.addWidget(self.cancelBtn)
        self.setFrameSource(None, None)

    def sizeHint(self):
        widgetHeights = (
            self.slider.height()
            + max([self.okayBtn.height(), self.cancelBtn.height()])
            + max([self.currentIndex.height(), self.currentTime.height()])
        )

        if isinstance(self.filters, BaseFilter):
            w, h = self.filters.width, self.filters.height
            sar = self.filters.sar

        elif isinstance(self.source, (Track, BaseFilter)):
            w, h = self.source.width, self.source.height
            sar = self.source.sar

        else:
            return super().sizeHint()

        dar = w*sar/h
        W, H = min([
            max([(w, h/sar), (w*sar, h)]),
            (960 - 8, (960 - 8)/dar),
            ((720 - 20 - widgetHeights)*dar, 720 - 20 - widgetHeights)
        ])

        return QSize(int(W + 8), int(H + 20 + widgetHeights))

    def setFrameSource(self, source, filters=None):
        self.source = source

        if source is not None:
            self.slider.setMaximum(self.source.framecount - 1)
            self.currentIndex.setMaximum(self.source.framecount - 1)
            self.filters = filters

            if self.filters is not None:
                lastpts = self.filters.pts_time[-1]
                self.slider.setSnapValues(self.filters.keyframes)

            else:
                lastpts = self.source.pts_time[-1]

                if isinstance(self.source, BaseFilter):
                    self.slider.setSnapValues(self.source.keyframes)

                else:
                    self.slider.setSnapValues(None)

            ms = int(lastpts*1000 + 0.5)
            s, ms = divmod(ms, 1000)
            m, s = divmod(s, 60)
            h, m = divmod(m, 60)
            self.currentTime.setMaximumTime(QTime(h, m, s, ms))
            self.slider.setValue(0)
            self._frameChange(0)

        else:
            self.slider.setSnapValues(None)

        self.update()

    def handleIndexChange(self, n):
        self._frameChange(n)

        self.slider.blockSignals(True)
        self.slider.setValue(n)
        self.slider.blockSignals(False)

    def handleSliderChange(self, n):
        self._frameChange(n)

        self.currentIndex.blockSignals(True)
        self.currentIndex.setValue(n)
        self.currentIndex.blockSignals(False)

    def _frameChange(self, n):
        if self.source is not None:
            if self.filters is not None:
                nn = n
                m = -1

                while m < 0 and nn < len(self.filters.indexMap):
                    m = self.filters.indexMap[nn]
                    nn += 1

                try:
                    pts = self.filters.pts_time[m]

                except Exception:
                    pts = None

                try:
                    frame = next(self.filters.iterFrames(
                        m, whence="framenumber"))

                except StopIteration:
                    frame = None

                sar = self.filters.sar

            else:
                try:
                    pts = self.source.pts_time[n]

                except IndexError:
                    pts = None

                try:
                    frame = next(self.source.iterFrames(
                        n, whence="framenumber"))

                except StopIteration:
                    frame = None

                sar = self.source.sar

            if frame is not None:
                im = frame.to_image()
                self.imageView.setFrame(im.toqpixmap())
                self.imageView.setSar(sar)

            if pts is not None:
                ms = int(pts*1000+0.5)
                s, ms = divmod(ms, 1000)
                m, s = divmod(s, 60)
                h, m = divmod(m, 60)

                self.currentTime.blockSignals(True)
                self.currentTime.setTime(QTime(h, m, s, ms))
                self.currentTime.blockSignals(False)

            self.frameSelectionChanged.emit(n, self.currentTime.time())

    def handleTimeChange(self, t):
        if self.source is not None:
            if self.filters is not None:
                pts = t.msecsSinceStartOfDay()/1000
                n = self.filters.frameIndexFromPtsTime(pts, dir="-")

            else:
                pts = t.msecsSinceStartOfDay()/1000
                n = self.source.frameIndexFromPtsTime(pts, dir="-")

            self.slider.blockSignals(True)
            self.slider.setValue(n)
            self.slider.blockSignals(False)

            self.currentIndex.blockSignals(True)
            self.currentIndex.setValue(n)
            self.currentIndex.blockSignals(False)

            self._frameChange(n)
Example #2
0
class QFrameSelect(QWidget):
    frameSelectionChanged = pyqtSignal(int, QTime)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        layout = QVBoxLayout()
        self.setLayout(layout)

        self.slider = Slider(self)
        self.slider.setOrientation(Qt.Horizontal)
        self.slider.valueChanged.connect(self._handleSliderChange)
        self.slider.setTickInterval(1)

        layout.addWidget(self.slider)

        hlayout = QHBoxLayout()
        layout.addLayout(hlayout)

        self.leftLabel = QLabel(self)

        self.spinBox = QSpinBox(self)
        self.spinBox.valueChanged.connect(self._handleSpinboxChange)

        self.currentTime = QTimeEdit(self)
        self.currentTime.setDisplayFormat("H:mm:ss.zzz")
        self.currentTime.timeChanged.connect(self._handleTimeEditChange)
        self.currentTime.editingFinished.connect(self._handleTimeEditFinished)

        self.rightLabel = QLabel(self)

        hlayout.addWidget(self.leftLabel)
        hlayout.addStretch()
        hlayout.addWidget(QLabel("Frame index:", self))
        hlayout.addWidget(self.spinBox)
        hlayout.addWidget(QLabel("Timestamp:", self))
        hlayout.addWidget(self.currentTime)
        hlayout.addStretch()
        hlayout.addWidget(self.rightLabel)

        self.setPtsTimeArray(None)

    def setValue(self, n):
        self.slider.setValue(n)

    def setMinimum(self, n=0):
        self.slider.setMinimum(n)
        self.spinBox.setMinimum(n)

        ms = int(self.pts_time[n] * 1000 + 0.5)
        s, ms = divmod(ms, 1000)
        m, s = divmod(s, 60)
        h, m = divmod(m, 60)

        self.currentTime.setMinimumTime(QTime(h, m, s, ms))
        self.leftLabel.setText(f"{n} ({h}:{m:02d}:{s:02d}.{ms:03d})")

    def setMaximum(self, n=None):
        if n is None:
            n = len(self.pts_time) - 1

        self.slider.setMaximum(n)
        self.spinBox.setMaximum(n)

        ms = int(self.pts_time[n] * 1000 + 0.5)
        s, ms = divmod(ms, 1000)
        m, s = divmod(s, 60)
        h, m = divmod(m, 60)

        self.currentTime.setMaximumTime(QTime(h, m, s, ms))
        self.rightLabel.setText(f"{n} ({h}:{m:02d}:{s:02d}.{ms:03d})")

    def setStartEndVisible(self, value):
        self.leftLabel.setHidden(not bool(value))
        self.rightLabel.setHidden(not bool(value))

    def setPtsTimeArray(self, pts_time=None):
        self.pts_time = pts_time

        if pts_time is not None:
            N = len(pts_time)
            self.setMinimum(0)
            self.setMaximum(N - 1)

            self.slider.setValue(0)
            self.slider.setSnapValues(None)

            self.slider.setDisabled(False)
            self.spinBox.setDisabled(False)
            self.currentTime.setDisabled(False)

        else:
            self.slider.setMinimum(0)
            self.slider.setMaximum(0)
            self.slider.setSnapValues(None)
            self.slider.setDisabled(True)
            self.spinBox.setDisabled(True)
            self.currentTime.setDisabled(True)

    def _handleSliderChange(self, n):
        self.spinBox.blockSignals(True)
        self.spinBox.setValue(n)
        self.spinBox.blockSignals(False)

        pts_time = self.pts_time[n]
        ms = int(pts_time * 1000 + 0.5)
        s, ms = divmod(ms, 1000)
        m, s = divmod(s, 60)
        h, m = divmod(m, 60)

        self.currentTime.blockSignals(True)
        self.currentTime.setTime(QTime(h, m, s, ms))
        self.currentTime.blockSignals(False)

        self.frameSelectionChanged.emit(n, QTime(h, m, s, ms))

    def _handleSpinboxChange(self, n):
        self.slider.blockSignals(True)
        self.slider.setValue(n)
        self.slider.blockSignals(False)

        pts_time = self.pts_time[n]
        ms = int(pts_time * 1000 + 0.5)
        s, ms = divmod(ms, 1000)
        m, s = divmod(s, 60)
        h, m = divmod(m, 60)

        self.currentTime.blockSignals(True)
        self.currentTime.setTime(QTime(h, m, s, ms))
        self.currentTime.blockSignals(False)

        self.frameSelectionChanged.emit(n, QTime(h, m, s, ms))

    def _handleTimeEditChange(self, t):
        pts = t.msecsSinceStartOfDay() / 1000

        try:
            n = search(self.pts_time, pts + 0.0005, dir="-")

        except IndexError:
            n = 0

        if n != self.slider.value():
            self.slider.blockSignals(True)
            self.slider.setValue(n)
            self.slider.blockSignals(False)

            self.spinBox.blockSignals(True)
            self.spinBox.setValue(n)
            self.spinBox.blockSignals(False)

            pts_time = self.pts_time[n]
            ms = int(pts_time * 1000 + 0.5)
            s, ms = divmod(ms, 1000)
            m, s = divmod(s, 60)
            h, m = divmod(m, 60)

            self.frameSelectionChanged.emit(n, QTime(h, m, s, ms))

    def _handleTimeEditFinished(self):
        t = self.currentTime.time()
        pts = t.msecsSinceStartOfDay() / 1000
        n = search(self.pts_time, pts + 0.0005, dir="-")
        pts_time = self.pts_time[n]

        ms = int(pts_time * 1000 + 0.5)
        s, ms = divmod(ms, 1000)
        m, s = divmod(s, 60)
        h, m = divmod(m, 60)
        T = QTime(h, m, s, ms)

        if t != T:
            self.currentTime.setTime(T)