0

I have a working QMediaPlayer that automatically scales the video to the size of the window. Now I want to modify that QMediaPlayer so that it scales a 180-degree rotated video to the size of the window.

I used the first solution in How to rotate video in QVideoWidget in PyQt5 to rotate the video. This meant introducing QGraphicsScene and QGraphicsView into the script. However, the video did not scale to the available window space. I tried the QGraphicsView method fitInView() to scale the rotated image, but that doesn’t seem to be the solution.

The script below opens two players, (1) the player that scales the unrotated video and (2) the player that players the rotated video but doesn’t scale it.

How can I modify the second player so that it rotates and scales the video to fit the available window size?

from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView
from PyQt5.QtGui import QPalette
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtMultimediaWidgets import QGraphicsVideoItem, QVideoWidget
import sys

class Player(QWidget):
    def __init__(self):
        super().__init__()
        
        self.setGeometry(350, 100, 700, 500)
        self.setWindowTitle('Non-Rotated Player')
 
        p = self.palette()
        p.setColor(QPalette.Window, Qt.black)
        self.setPalette(p)

        #create media player object
        self.player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.player.setNotifyInterval(17)

        #create videowidget object 
        self.videowidget = QVideoWidget()

        #create vbox layout
        vboxLayout = QVBoxLayout()
        vboxLayout.addWidget(self.videowidget)
 
        self.setLayout(vboxLayout)
 
        self.player.setVideoOutput(self.videowidget)
        
        self.filename = 'my_video.MP4'
        self.player.setMedia(QMediaContent(QUrl.fromLocalFile(self.filename)))
        self.player.setMuted(True)
        self.player.play()

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

        self.setWindowTitle('Rotated Player')
        self.setGeometry(350, 100, 700, 500)
 
        p = self.palette()
        p.setColor(QPalette.Window, Qt.black)
        self.setPalette(p)

        #create media player object
        self.player = QMediaPlayer(self, QMediaPlayer.VideoSurface)
        
        # Create the surface and view
        self.scene = QGraphicsScene(self)
        self.gv = QGraphicsView(self.scene)
        self.gv.setStyleSheet('background-color: black;')
        
        # Create the item
        self.videoitem = QGraphicsVideoItem()
        self.scene.addItem(self.videoitem)
        self.videoitem.setRotation(180)
        
        # Shouldn't this scale the item to fit the view???
        self.player.setVideoOutput(self.videoitem)
        self.gv.fitInView(self.videoitem, Qt.IgnoreAspectRatio)

        vboxLayout = QVBoxLayout()
        vboxLayout.addWidget(self.gv)
 
        self.setLayout(vboxLayout)
        
        self.filename = 'my_video.MP4'
    
        self.player.setMedia(QMediaContent(QUrl.fromLocalFile(self.filename)))
        self.player.setMuted(True)
        self.player.play()
                
if __name__ == '__main__':
    
    app = QApplication(sys.argv)
    p = Player() 
    p.show()
    r = RotatedPlayer()
    r.show()
    app.exec_()
slalomchip
  • 779
  • 2
  • 9
  • 25
  • `QMediaScene`? `QMediaView`? Those classes don't exist, and there's not even any reference to them in your code. – musicamante Jun 30 '22 at 02:00
  • @musicamante - My mistake - question has been corrected - should have been QGraphicsView and QGraphicsScene – slalomchip Jun 30 '22 at 02:03
  • Calling `fitInView()` in the `__init__` is actually pointless. At that point, the widget has usually not been mapped yet, so it uses an arbitrrary size (100x30 for widgets created with a parent, 600x480 for any other case; both are valid unless specific size constrains are *excplicitly* set through setMinimum/Maximum/Fixed Size/Width/Height). `fitInView()` should be called in the override of the `resizeEvent()` of the widget subclass, or, eventually, in an event filter. – musicamante Jun 30 '22 at 02:19

1 Answers1

0

@musicamante solved it in his comment. This response documents my interpretation of his suggestion.

In __init__(), the line:

self.gv.fitInView(self.videoitem, Qt.KeepAspectRatio)

doesn't do anything. It needs to be moved to a resizeEvent() override.

After removing the line from __init__() and creating the following resizeEvent() override, the video is rotated and resized as expected.

def resizeEvent(self, event):
    super(RotatedPlayer, self).resizeEvent(event)
    self.gv.fitInView(self.videoitem, Qt.KeepAspectRatio)
slalomchip
  • 779
  • 2
  • 9
  • 25
  • Note: in this case itll doesn't really matter, since the default implementation of QWidget does nothing in `resizeEvent()`, but remember that it's always good practice to call the base implementation of overridden methods, unless you *really* know what you're doing. – musicamante Jun 30 '22 at 08:18
  • Got it. I modified the solution based on your comment. Hopefully this is what you meant – slalomchip Jun 30 '22 at 20:54