Jaime02
Jaime02

Reputation: 347

Crop QGraphicsVideoItem

I have a QGraphicsVideoItem added to a QGraphicsView widget. How can I crop the item so I don't see part of the displayed video? I have tried doing this:

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

        scene = QGraphicsScene()
        self.video_view = QGraphicsView(self)
        self.video_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.video_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.video_view.setScene(scene)

        player_1 = QMediaPlayer(self)
        self.video_item_1 = QGraphicsVideoItem()
        player_1.setVideoOutput(self.video_item_1)
        scene.addItem(self.video_item_1)
        
        # player_1.setSource("V.mp4")
        # player_1.play()

        player_2 = QMediaPlayer(self)
        self.video_item_2 = QGraphicsVideoItem()
        self.video_item_2.setAspectRatioMode(Qt.KeepAspectRatio)
        player_2.setVideoOutput(self.video_item_2)
        scene.addItem(self.video_item_2)

        # player_2.setSource("V_compressed.mp4")
        # player_2.play()

        self.slider = QSlider(Qt.Horizontal, self)

        self.slider.setStyleSheet("""
        QSlider::groove:horizontal {
            background: transparent;
        }
        
        QSlider::handle:horizontal {
            background: white;
            border: 1px solid black;
            border-radius: 3px;
            width: 10px;
        }
        """)
        self.slider.valueChanged.connect(self.crop_video)

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self.video_view.resize(self.size())

        self.slider.resize(self.size())
        self.slider.setRange(0, self.video_view.width())

        self.video_item_1.setSize(self.video_view.size())
        self.video_item_2.setSize(self.video_view.size())

        self.crop_video()

    def crop_video(self):
        # Crop the video substracting the slider right area
        self.video_item_2.setPos(self.slider.value(), 0)
        self.video_item_2.setOffset(QPoint(-self.slider.value(), 0))

But the part of the video that has negative X coordinate is still drawn. How can I hide it?

(I don't use QVideoWidget because I need to have the slider over the video, and it seems that the video is always on top of other widgets. I could use QVideoWidget if there is no other choice and put the slider outside the videos)

Upvotes: 2

Views: 232

Answers (1)

Jaime02
Jaime02

Reputation: 347

After a lot of trial error and wander through docs, I found that I could override paint method of a QGraphicsVideo item and set there the QPainter (setClipRect)

Full code:

from PySide6.QtCore import Qt, QRectF
from PySide6.QtGui import QPainter
from PySide6.QtMultimedia import QMediaPlayer
from PySide6.QtMultimediaWidgets import QGraphicsVideoItem
from PySide6.QtWidgets import (
    QWidget,
    QApplication,
    QMainWindow,
    QVBoxLayout,
    QLabel,
    QSlider,
    QSizePolicy,
    QGraphicsScene,
    QGraphicsView,
    QStyleOptionGraphicsItem,
)


class CustomItem(QGraphicsVideoItem):
    def __init__(self):
        super().__init__()
        self.offset = 0

    def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget: QWidget):
        if self.offset != 0:
            painter.setClipRect(QRectF(self.offset, 0, self.size().width(), self.size().height()))
        QGraphicsVideoItem.paint(self, painter, option, widget)


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

        scene = QGraphicsScene()
        self.video_view = QGraphicsView(self)
        self.video_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.video_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.video_view.setScene(scene)

        player_1 = QMediaPlayer(self)
        self.video_item_1 = CustomItem()
        player_1.setVideoOutput(self.video_item_1)
        scene.addItem(self.video_item_1)

        # player_1.setSource("V.mp4")  # Set here your video source
        player_1.play()

        player_2 = QMediaPlayer(self)
        self.video_item_2 = CustomItem()
        self.video_item_2.setAspectRatioMode(Qt.KeepAspectRatio)
        player_2.setVideoOutput(self.video_item_2)
        scene.addItem(self.video_item_2)

        # player_2.setSource("V_compressed.mp4") # Set here your video source
        player_2.play()

        self.slider = QSlider(Qt.Horizontal, self)

        self.slider.setStyleSheet("""
        QSlider::groove:horizontal {
            background: transparent;
        }

        QSlider::handle:horizontal {
            background: white;
            border: 1px solid black;
            border-radius: 3px;
            width: 10px;
        }
        """)
        self.slider.valueChanged.connect(self.crop_video)

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self.video_view.resize(self.size())

        self.slider.resize(self.size())
        self.slider.setRange(0, self.video_view.width())

        self.video_item_1.setSize(self.video_view.size())
        self.video_item_2.setSize(self.video_view.size())

        self.crop_video()

    def crop_video(self):
        # Crop the video substracting the slider left area
        self.video_item_2.offset = self.slider.value()


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        self.main_layout = QVBoxLayout()
        self.central_widget.setLayout(self.main_layout)

        self.label = QLabel("Overlay")
        self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.main_layout.addWidget(self.label)

        self.overlay_widget = OverlayWidget()
        self.overlay_widget.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.main_layout.addWidget(self.overlay_widget)

        self.resize(800, 600)


if __name__ == "__main__":
    app = QApplication()
    window = MainWindow()
    window.show()
    app.exec()

Upvotes: 1

Related Questions