You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt中QGraphicsItem与QGraphicsView边缘对齐及视频布局问题

解决QGraphicsView视频播放器OSC控件布局问题

问题解答

1. fitInView后无法获取视频实际显示尺寸?

没错,fitInView仅在视图层面应用缩放变换,不会改变QGraphicsVideoItem自身的size()返回值(始终是视频原始分辨率)。要获取视频在视图中实际显示的尺寸,需要通过视图的变换矩阵计算:

# 获取视频项在视图坐标系中的边界矩形
view_rect = self.mapFromScene(self._video_item.mapToScene(self._video_item.boundingRect()))
# 实际显示的宽高
display_width = view_rect.width()
display_height = view_rect.height()

或者直接通过视图的缩放因子乘以视频原始尺寸:

scale_factor = self.transform().m11()  # x轴缩放因子,保持宽高比下y轴一致
display_width = self._video_item.size().width() * scale_factor

2. 如何正确设置ControlBar的位置和尺寸?

因为ControlBar设置了ItemIgnoresTransformations,它的坐标系是视图的像素坐标系(不受视图缩放影响),核心逻辑如下:

  • 先获取视频在视图中显示的实际宽度,将ControlBar的宽度设为该值
  • 将ControlBar水平居中于视频下方,垂直紧贴视图底部
    关键代码逻辑:
# 获取视频在视图中的显示区域
video_scene_rect = self._video_item.mapToScene(self._video_item.boundingRect()).boundingRect()
video_display_rect = self.mapFromScene(video_scene_rect).boundingRect()
view_rect = self.viewport().rect()

# 设置ControlBar宽度与视频显示宽度一致
self._control_bar.setFixedWidth(video_display_rect.width())
self._control_bar.setFixedHeight(100)

# 定位:水平居中,垂直紧贴视图底部
bar_x = (view_rect.width() - video_display_rect.width()) // 2
bar_y = view_rect.height() - 100
scene_pos = self.mapToScene(qtc.QPoint(bar_x, bar_y))
self._proxy_control_bar.setPos(scene_pos.x(), scene_pos.y())

3. 为何出现大量多余空白区域?

主要有两个原因:

  • QGraphicsScene的默认范围会自动包含所有添加的Item,当ControlBar位置超出视频项范围时,场景会自动扩大,导致视图显示多余空白
  • 多次调用fitInView时,若场景包含ControlBar,会把整个场景纳入适配范围,导致视图缩放异常
    解决方法:
  • 手动设置Scene范围为视频项的边界:self._scene.setSceneRect(self._video_item.boundingRect())
  • 确保fitInView仅针对视频项,不包含ControlBar

修改后的完整代码

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")
        # 固定高度100px
        self.setFixedHeight(100)

class View(qtw.QGraphicsView):
    def __init__(self) -> None:
        super().__init__()
        self.setMouseTracking(True)
        self.setRenderHints(qtg.QPainter.RenderHint.SmoothPixmapTransform | qtg.QPainter.RenderHint.Antialiasing)
        self.setFrameStyle(qtw.QFrame.Shape.NoFrame)
        self.setViewportMargins(0, 0, 0, 0)

        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(qtc.QUrl.fromLocalFile("test.mp4"))
        # 视频加载完成后初始化布局
        self._media_player.mediaStatusChanged.connect(self._on_media_loaded)

    def _on_media_loaded(self, status):
        if status == qtm.QMediaPlayer.MediaStatus.LoadedMedia:
            # 设置场景范围为视频原始尺寸
            self._scene.setSceneRect(self._video_item.boundingRect())
            self.fitInView(self._video_item, qtc.Qt.AspectRatioMode.KeepAspectRatio)
            self._adjust_control_bar()

    def _adjust_control_bar(self):
        if not self._video_item.boundingRect().isNull():
            # 获取视频在视图中的显示区域
            video_scene_rect = self._video_item.mapToScene(self._video_item.boundingRect()).boundingRect()
            video_display_rect = self.mapFromScene(video_scene_rect).boundingRect()
            view_rect = self.viewport().rect()

            # 设置ControlBar宽度与视频显示宽度一致
            self._control_bar.setFixedWidth(video_display_rect.width())

            # 定位:水平居中,垂直紧贴视图底部
            bar_x = (view_rect.width() - video_display_rect.width()) // 2
            bar_y = view_rect.height() - self._control_bar.height()
            scene_pos = self.mapToScene(qtc.QPoint(bar_x, bar_y))
            self._proxy_control_bar.setPos(scene_pos.x(), scene_pos.y())

    def resizeEvent(self, event) -> None:
        super().resizeEvent(event)
        self.fitInView(self._video_item, qtc.Qt.AspectRatioMode.KeepAspectRatio)
        self._adjust_control_bar()

app = qtw.QApplication()
view = View()
view.show()
app.exec()

内容的提问来源于stack exchange,提问作者Neat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 13:25:17