I'm trying to create a video player similar to the looks of the default GUI for mpv. I'm using a QGraphicsVideoItem
inside a QGraphicsView
along with a custom ControlBar
widget as the OSC.
I want the OSC to be 100px high and video.width()
px wide, and always flush with the bottom edge of the QGraphicsView
widget. I can't seem to do either of those requirements.
MRE:
from PySide6 import QtWidgets as qtw
from PySide6 import QtGui as qtg
from PySide6 import QtCore as qtc
from PySide6 import QtMultimedia as qtm
from PySide6 import QtMultimediaWidgets as qtmw
class ControlBar(qtw.QWidget):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.setStyleSheet("background: red")
class View(qtw.QGraphicsView):
def __init__(self) -> None:
super().__init__()
self.setMouseTracking(True)
self.setRenderHints(qtg.QPainter.RenderHint.SmoothPixmapTransform | qtg.QPainter.RenderHint.Antialiasing)
self.setViewportMargins(-2, -2, -2, -2) # `QGraphicsView` has hard coded margins.
self.setFrameStyle(qtw.QFrame.Shape.NoFrame)
self._scene = qtw.QGraphicsScene()
self._video_item = qtmw.QGraphicsVideoItem()
self._control_bar = ControlBar()
self._media_player = qtm.QMediaPlayer()
self._scene.addItem(self._video_item)
self._proxy_control_bar = self._scene.addWidget(self._control_bar)
self._proxy_control_bar.setFlag(qtw.QGraphicsItem.GraphicsItemFlag.ItemIgnoresTransformations)
self.setScene(self._scene)
self._media_player.setVideoOutput(self._video_item)
self._media_player.setSource("video")
self._media_player.mediaStatusChanged.connect(self._media_player.play)
def showEvent(self, event) -> None:
qtc.QTimer.singleShot(100, lambda: self.fitInView(self._video_item, qtc.Qt.AspectRatioMode.KeepAspectRatio))
def resizeEvent(self, event) -> None:
self._proxy_control_bar.setGeometry(0, 0, self.viewport().width(), 100)
pos = qtc.QPoint(0, self.height() - self._proxy_control_bar.size().height())
self._proxy_control_bar.setPos(0, self.mapToScene(pos).y())
self.fitInView(self._video_item, qtc.Qt.AspectRatioMode.KeepAspectRatio)
app = qtw.QApplication()
view = View()
view.show()
app.exec()
I've been able to set the height of the widget to 100px, but using control_area.setGeometry(..., ..., self.viewport().width(), ...)
sets the width to be a bit more than the video's width. And, for some reason, adding self._control_bar
to the scene creates all this extra empty space around the two items, I have no idea why.
My questions are,
- is there no way to get the actual size (specifically the width) of the video item after a
fitInView
call?
Because callingitem.size()
even after afitInView
call just returns the original size of the item, which I guess makes sense since only the view's view of the item was "fit in view" and the item itself is still the same. - How do I set the position of the
control_bar
to be where I want it to?
As seen in one of the videos below, the way I'm doing it right now does not accomplish it at all. - What's up with all the extra empty space?
How it looks: