PySide6 OpenCV: Full Camera Feed in QGraphicsView No Crop
Fix cropped OpenCV camera feed in PySide6 QGraphicsView. Set scene rect to pixmap bounds, call fitInView on resize, disable scrollbars, and use anchors for uncropped display with Qt.KeepAspectRatio.
How to display full OpenCV camera feed without cropping in PySide6 QGraphicsView?
I created a GUI using Qt Designer: a MainWindow with a central widget containing only a QGraphicsView.
Using PySide6 and OpenCV, I capture the camera feed and display it in the QGraphicsView, but it shows a cropped version of the stream instead of the full scene.
Here’s the code:
from Ui.uiLoader import load_ui
from PySide6.QtWidgets import QWidget, QGraphicsScene, QGraphicsPixmapItem
from PySide6.QtCore import QTimer, Qt
from PySide6.QtGui import QImage, QPixmap, QPen
import cv2
class MainWindowTest:
def __init__(self):
# Load main window
self.ui = load_ui("graphicsviewTest.ui")
self.graphicsview = self.ui.graphicsView
self.scene = QGraphicsScene()
self.pixmap_item = QGraphicsPixmapItem()
self.scene.addItem(self.pixmap_item)
self.graphicsview.setScene(self.scene)
# Persistent camera object
self.cap = cv2.VideoCapture(0)
# Timer drives the video stream
self.timer = QTimer()
self.timer.timeout.connect(self.update_frame)
self.timer.start(30) # ~33 FPS
def update_frame(self):
ret, frame = self.cap.read()
if not ret:
return
qt_image = self.convert_cv_qt(frame)
pixmap = QPixmap.fromImage(qt_image)
self.pixmap_item.setPixmap(pixmap)
self.scene.setSceneRect(pixmap.rect())
self.graphicsview.fitInView(self.scene.sceneRect(),Qt.KeepAspectRatio)
def convert_cv_qt(self, frame):
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgb.shape
bytes_per_line = ch * w
image = QImage(
rgb.data,
w,
h,
bytes_per_line,
QImage.Format_RGB888
)
return image.copy()
The code runs, but the GraphicsView displays a cropped camera stream. Despite using fitInView with Qt.KeepAspectRatio, it doesn’t show the entire camera scene. How can I fix this to display the full uncropped feed?
Set the QGraphicsScene rect to match the camera pixmap bounds, stop forcing a fit on every frame, and re-run fitInView only when the image size changes or when the view is resized. Also disable scrollbars, set the transformation/resize anchors, and enable smooth pixmap scaling — together these steps make a PySide6 OpenCV camera feed display uncropped in QGraphicsView.
Contents
- QGraphicsView FitInView — Quick fix for PySide6 OpenCV feed
- Why the QGraphicsView shows a cropped OpenCV stream
- Robust PySide6 + OpenCV example (no cropping)
- Integrating with Qt Designer (promote or replace the view)
- Debug checklist: verify what’s being shown
- Sources
- Conclusion
QGraphicsView FitInView — Quick fix for PySide6 OpenCV feed
If you want a minimal edit to your existing code, do three things:
- Make sure the scene rect exactly matches the pixmap bounds (use a QRectF of the pixmap).
- Don’t call
fitInView()on every frame; call it once when the image size changes and/or from the view’sresizeEvent. - Set anchors and turn off scrollbars so the transform doesn’t “clip” the result.
Minimal patch to your code (only the changed parts shown):
from PySide6.QtCore import QRectF, Qt
from PySide6.QtGui import QPainter
from PySide6.QtWidgets import QGraphicsView
# in __init__ after setting self.graphicsview
self.graphicsview.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.graphicsview.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.graphicsview.setTransformationAnchor(QGraphicsView.AnchorViewCenter)
self.graphicsview.setResizeAnchor(QGraphicsView.AnchorViewCenter)
self.graphicsview.setRenderHints(QPainter.SmoothPixmapTransform)
self._last_pixmap_size = None
# in update_frame, replace your fitInView call with:
self.pixmap_item.setPixmap(pixmap)
self.pixmap_item.setOffset(0, 0) # ensure item at 0,0
rect = QRectF(pixmap.rect()) # QRectF(width, height)
if self.scene.sceneRect() != rect:
self.scene.setSceneRect(rect)
self.graphicsview.fitInView(rect, Qt.KeepAspectRatio)
# do NOT call fitInView every frame; resizeEvent handles view-size changes
Why this helps: scene.setSceneRect() gives fitInView() a precise rectangle to scale to the view. Calling fitInView() only when the pixmap size changes (and again when the view is resized) prevents repeated transforms and accidental clipping.
For the authoritative behavior of QGraphicsView and sceneRect, see the official docs: https://doc.qt.io/qt-6/qgraphicsview.html and PySide6-specific reference: https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QGraphicsView.html.
Why the QGraphicsView shows a cropped OpenCV stream
What’s actually going on? A couple of common causes:
- Wrong rectangle: if the scene rect doesn’t match the pixmap bounds,
fitInView()may be told to fit a different area, so part of your image falls outside the rectangle that’s being scaled into the viewport. - Timing: calling
fitInView()before the view has a proper size (for example, during startup) or calling it every frame can leave the view transform in an unexpected state. - Anchors and scrollbars: the default transform/resize anchors and visible scrollbars affect how the view fits content; scrollbars can make the visible area smaller (appearing as cropping).
- Coordinates: if your pixmap item isn’t at (0,0) or uses an offset, the scene rect may not enclose the visible pixmap.
A reliable remedy is to set the scene rect from the pixmap, set the view anchors and scrollbars, and re-run fitInView() when the view size or image size actually changes. StackOverflow discussions and Qt docs point to calling fitInView from the resize event or a view subclass so the scale is recalculated when the widget’s viewport changes size (example advice: https://stackoverflow.com/questions/79859724/how-to-show-cemra-feed-without-cropping-in-a-program-that-is-qt-designed-and-wri).
Robust PySide6 + OpenCV example (no cropping)
Below is a self-contained pattern that works reliably: subclass the QGraphicsView to call fitInView() on resize, update the scene rect only when the frame size changes, and enable smooth scaling.
import sys
import cv2
from PySide6.QtWidgets import (
QApplication, QMainWindow, QGraphicsScene, QGraphicsPixmapItem, QGraphicsView
)
from PySide6.QtCore import QTimer, QRectF, Qt
from PySide6.QtGui import QImage, QPixmap, QPainter
class FitView(QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
self.setTransformationAnchor(QGraphicsView.AnchorViewCenter)
self.setResizeAnchor(QGraphicsView.AnchorViewCenter)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setRenderHints(QPainter.SmoothPixmapTransform)
def resizeEvent(self, event):
super().resizeEvent(event)
scene = self.scene()
if scene and not scene.sceneRect().isNull():
# Recalculate transform so the sceneRect fits the new viewport
self.fitInView(scene.sceneRect(), Qt.KeepAspectRatio)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.view = FitView()
self.setCentralWidget(self.view)
self.scene = QGraphicsScene()
self.pixmap_item = QGraphicsPixmapItem()
self.scene.addItem(self.pixmap_item)
self.view.setScene(self.scene)
self.cap = cv2.VideoCapture(0)
self.timer = QTimer(self)
self.timer.timeout.connect(self.update_frame)
self.timer.start(30)
def update_frame(self):
ret, frame = self.cap.read()
if not ret:
return
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgb.shape
bytes_per_line = ch * w
qimage = QImage(rgb.data, w, h, bytes_per_line, QImage.Format_RGB888).copy()
pixmap = QPixmap.fromImage(qimage)
# Place pixmap, keep item at 0,0, and update scene rect only when size changes
self.pixmap_item.setPixmap(pixmap)
self.pixmap_item.setOffset(0, 0)
rect = QRectF(pixmap.rect())
if self.scene.sceneRect() != rect:
self.scene.setSceneRect(rect)
# Fit once now that the scene rect changed;
# future viewport resizes are handled in FitView.resizeEvent
self.view.fitInView(rect, Qt.KeepAspectRatio)
def closeEvent(self, event):
self.timer.stop()
self.cap.release()
super().closeEvent(event)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWindow()
w.resize(800, 600)
w.show()
sys.exit(app.exec())
This pattern follows guidance in the Qt docs and tutorials: set the scene rect from the pixmap bounds and handle resizing in a view subclass rather than relying on repeated per-frame fits (see the official docs: https://doc.qt.io/qt-6/qgraphicsview.html and a practical OpenCV+Qt example: https://amin-ahmadi.com/2018/03/29/how-to-read-process-and-display-videos-using-qt-and-opencv/).
Integrating with Qt Designer (promote or replace the view)
Two practical ways to apply the FitView behavior when you built your UI in Designer:
- Promote the QGraphicsView in Designer
- Right‑click the QGraphicsView → Promote to… → set Promoted class name to
FitViewand the header/module to the Python module that definesFitView. - Your Python code then just imports the module containing
FitViewbefore loading the UI.
- Replace at runtime in code (if you prefer not to change the .ui): remove the Designer widget and insert a
FitViewinstance into the same layout and position:
old = self.ui.graphicsView
parent = old.parentWidget()
layout = parent.layout()
idx = layout.indexOf(old)
layout.removeWidget(old)
old.deleteLater()
fit = FitView(parent)
fit.setObjectName("graphicsView") # keep the same objectName if needed
layout.insertWidget(idx, fit)
self.graphicsview = fit
# then attach scene/pixmap_item to self.graphicsview as shown above
Promoting the widget in Designer is usually the cleanest option.
Debug checklist: verify what’s being shown
If you still get cropping, run these quick checks:
- Print sizes:
print("viewport:", view.viewport().size(), "sceneRect:", scene.sceneRect())— do they match expectations? - Is the pixmap at (0,0)?
self.pixmap_item.setPos(0,0)orsetOffset(0,0). - Are scrollbars visible? If yes, call
setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)and vertical likewise. - Try
fitInView(self.pixmap_item, Qt.KeepAspectRatio)(some examples use item overload) orensureVisible(scene.sceneRect())beforefitInView. Examples and community fixes often mention callingfitInView()fromresizeEvent; see this discussion: https://stackoverflow.com/questions/79859724/how-to-show-cemra-feed-without-cropping-in-a-program-that-is-qt-designed-and-wri and tips on fitting without changing aspect ratio: https://stackoverflow.com/questions/9654222/how-to-fit-in-view-the-pixmaps-in-qgraphicsview-qgraphicsscene-without-changing.
If you don’t need QGraphicsView features, a simpler approach is to use a QLabel and pixmap.scaled(label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation), but QGraphicsView gives more flexibility if you plan overlays, shapes or transformations.
Sources
- QGraphicsView Class | Qt Widgets | Qt 6.10.1
- PySide6.QtWidgets.QGraphicsView - Qt for Python
- How to Read, Process and Display Videos Using Qt and OpenCV - Amin
- How to show cemra feed without cropping in a program that is Qt designed and writen in python? - Stack Overflow
- Display Camera in pyqt - Stack Overflow
- QT, C++: fast way to draw live image stream from camera on QGraphicsview - Stack Overflow
- How to fit in view the pixmaps in QGraphicsView/QGraphicsScene without changing aspect ratio - Stack Overflow
- Displaying webcam feed using OpenCV and Python+PySide · GitHub Gist
- QGraphics vector graphics interfaces with Python and PySide6 (PythonGUIs)
Conclusion
Match the scene rectangle to your pixmap, avoid calling fitInView() on every frame, and recalculate the fit when either the image size or the view size changes (best done in a QGraphicsView subclass or the view’s resizeEvent). With anchors set and scrollbars disabled, your PySide6 OpenCV camera feed will display full and uncropped inside QGraphicsView.