PyQt中QGraphicsView与QGraphicsVideoItem等尺寸适配问题
解决方案
问题根源
当前代码的核心问题是视频初始化阶段还未获取到有效尺寸,就在initUI里直接调用self.video_widget.setSize(self.mediaPlayer.videoSink().videoSize()),此时videoSize()可能返回空或默认尺寸,导致初始显示异常。另外,resizeFunc里的尺寸计算逻辑绕了弯路,没必要用mapFromScene转换,直接用视频原始尺寸和视图的缩放关系处理更准确。
修正后的代码
import sys from PySide6.QtCore import * from PySide6.QtGui import * from PySide6.QtWidgets import * from PySide6.QtMultimediaWidgets import QGraphicsVideoItem from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput class MyWindow(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.scene = QGraphicsScene() self.view = QGraphicsView(self.scene) # 关闭滚动条,避免视频适配时出现滚动 self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # 开启渲染优化 self.view.setRenderHint(QPainter.Antialiasing) self.video_widget = QGraphicsVideoItem() self.scene.addItem(self.video_widget) self.mediaPlayer = QMediaPlayer() self.audioOutput = QAudioOutput() self.mediaPlayer.setAudioOutput(self.audioOutput) self.mediaPlayer.setVideoOutput(self.video_widget) # 监听视频尺寸变化信号,等视频加载完成后再获取有效尺寸 self.mediaPlayer.videoSink().videoSizeChanged.connect(self.onVideoSizeChanged) self.mediaPlayer.setSource(QUrl.fromLocalFile("potrait.mp4")) # 添加可交互文本元素 self.text = QGraphicsTextItem("test") self.text.setFlags(QGraphicsTextItem.ItemIsSelectable | QGraphicsTextItem.ItemIsMovable | QGraphicsTextItem.ItemIsFocusable) font = QFont() font.setPointSize(100) self.text.setFont(font) self.scene.addItem(self.text) layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) # 去掉布局边距,让视图填满窗口 layout.addWidget(self.view) self.mediaPlayer.play() def onVideoSizeChanged(self, size): if size.isValid(): # 设置视频项尺寸为原始视频尺寸 self.video_widget.setSize(size) # 更新场景范围为视频尺寸,确保视图适配基准正确 self.scene.setSceneRect(QRectF(QPointF(0, 0), size)) # 立即触发视图适配 self.adjustViewToVideo() def adjustViewToVideo(self): # 让视频按宽高比填满可用区域,优先扩展填充 self.view.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatioByExpanding) # 获取适配后的视图内容尺寸,设置窗口最小尺寸 fit_rect = self.view.mapFromScene(self.scene.sceneRect()).boundingRect() self.setMinimumSize(fit_rect.width(), fit_rect.height()) # 首次显示时直接将窗口设为适配后的尺寸 if not self.isVisible(): self.resize(fit_rect.width(), fit_rect.height()) def resizeEvent(self, event): self.adjustViewToVideo() if __name__ == "__main__": app = QApplication(sys.argv) window = MyWindow() window.show() sys.exit(app.exec_())
关键修改点
- 监听视频尺寸信号:通过
videoSizeChanged在视频加载完成后获取有效尺寸,解决初始尺寸无效的问题。 - 优化适配逻辑:用
Qt.KeepAspectRatioByExpanding实现视频填满可用区域且保持宽高比,贴近Premiere的预览效果。 - 简化尺寸计算:移除冗余的
showEvent和复杂的坐标转换,直接基于场景矩形计算适配尺寸。 - 优化布局设置:去掉布局边距、关闭滚动条,让视图完全填充窗口,提升视觉连贯性。
效果说明
- 视频启动后自动以原始宽高比填满窗口,无需手动调整。
- 窗口大小变化时,视频自动适配并保持宽高比,始终填满可用区域。
- 场景尺寸由视频尺寸决定,文本元素基于视频坐标系进行交互。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

