NSAViewer — Desktop Photobooth App

Build a webcam photobooth app with Python and Qt to take snapshots from your camera.
Heads up! You've already completed this tutorial.

This app isn't actually a direct line from your webcam to the NSA — it's a demo of using the webcam and camera support in Qt with Python. The name is a nod to the paranoia (or is it...) of being watched through your webcam by government spooks.

With this Python photobooth app you can use your laptop's built-in webcam to view yourself and take photobooth-style snapshots. The app uses Qt's built-in camera classes to provide support for multiple cameras if you have them.

Originally the plan was to make the app (openly) upload snapshots to a remote server to complete the idea, but this is the internet, and nobody wants to see that.

Setting Up the Qt Camera Interface in Python

Camera support in Qt5 is accessible via QtMultimedia, with multimedia-specific widgets available via QtMultimediaWidgets. The first step to accessing your webcam is to get a list of currently available cameras on the system using QCameraInfo.availableCameras(). This returns a list of QCameraInfo objects, which provide various bits of information about each camera, including a unique ID.

If we have no cameras available we just quit out, ungracefully. If a camera is available we set up the QCameraViewfinder to provide a live updating viewfinder view from the active camera. We select the first camera in our list to use as a 'default' — on a laptop this is usually the built-in webcam.

python
class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.available_cameras = QCameraInfo.availableCameras()
        if not self.available_cameras:
            pass #quit

        self.viewfinder = QCameraViewfinder()
        self.viewfinder.show()
        self.setCentralWidget(self.viewfinder)

        # Set the default camera.
        self.select_camera(0)

This is all that is required to stream the active webcam feed live to the viewfinder widget.

Adding Camera Selection to the PyQt5 Toolbar

The toolbar allows a user to select the active camera, take snapshot photos, and select the output folder for these photos. Each QAction is connected to a custom slot to handle the specific behaviour.

The camera selection list is pre-filled with the user-friendly name of each camera, via QCameraInfo.description(). The index of the selected camera in the combobox matches its position in the list.

python
camera_toolbar = QToolBar("Camera")
camera_toolbar.setIconSize(QSize(14, 14))
self.addToolBar(camera_toolbar)

photo_action = QAction(QIcon(os.path.join('images', 'camera-black.png')), "Take photo...", self)
photo_action.setStatusTip("Take photo of current view")
photo_action.triggered.connect(self.take_photo)
camera_toolbar.addAction(photo_action)

change_folder_action = QAction(QIcon(os.path.join('images', 'blue-folder-horizontal-open.png')), "Change save location...", self)
change_folder_action.setStatusTip("Change folder where photos are saved.")
change_folder_action.triggered.connect(self.change_folder)
camera_toolbar.addAction(change_folder_action)

camera_selector = QComboBox()
camera_selector.addItems([c.description() for c in self.available_cameras])
camera_selector.currentIndexChanged.connect( self.select_camera )

camera_toolbar.addWidget(camera_selector)

Switching Between Multiple Webcams with QCamera

The camera select method accepts a single parameter i, which is the index of a camera in our prefilled self.available_cameras list. This is a QCameraInfo object, which can be passed to QCamera to create a new camera object.

Once the camera object is created, we set it to use our existing viewfinder widget (central widget). The capture mode is set to QCamera.CaptureStillImage and then the camera must be started with .start().

Capture of images from a camera is handled by QCameraImageCapture, which we set up by passing in our previously created camera object. The .imageCaptured signal is triggered every time (after) an image is captured, so we can connect to it to show a status update — the snapshotting is done separately.

python
def select_camera(self, i):
    self.camera = QCamera(self.available_cameras[i])
    self.camera.setViewfinder(self.viewfinder)
    self.camera.setCaptureMode(QCamera.CaptureStillImage)
    self.camera.error.connect(lambda: self.alert(self.camera.errorString()))
    self.camera.start()

    self.capture = QCameraImageCapture(self.camera)
    self.capture.error.connect(lambda i, e, s: self.alert(s))
    self.capture.imageCaptured.connect(lambda d, i: self.status.showMessage("Image %04d captured" % self.save_seq))

    self.current_camera_name = self.available_cameras[i].description()
    self.save_seq = 0

Taking a Photo with QCameraImageCapture

Taking a webcam snapshot is handled in our custom take_photo slot using the QCameraImageCapture object created when initialising the camera. The .capture() method accepts a filename, which we create using our selected save path and a full-name timestamp. The file is stamped with the current time, plus the current camera name and a sequence number to avoid conflicts. Snapshots are saved in JPEG format.

python
def take_photo(self):
    timestamp = time.strftime("%d-%b-%Y-%H_%M_%S")
    self.capture.capture(os.path.join(self.save_path, "%s-%04d-%s.jpg" % (
        self.current_camera_name,
        self.save_seq,
        timestamp
    )))
    self.save_seq += 1

The sequence number is incremented after the snapshot is taken.

With just these few components — QCamera, QCameraViewfinder, and QCameraImageCapture — you have a fully functional Python photobooth application built with PyQt5. You can extend this further by adding image filters, countdown timers, or multi-shot burst modes.

Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

Create GUI Applications with Python & Qt6 by Martin Fitzpatrick

(PySide6 Edition) The hands-on guide to making apps with Python — Over 15,000 copies sold!

More info Get the book

Martin Fitzpatrick

NSAViewer — Desktop Photobooth App was written by Martin Fitzpatrick.

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt. Martin founded PythonGUIs to provide easy to follow GUI programming tutorials to the Python community. He has written a number of popular Python books on the subject.