0

I am creating an application that reads the input of a webcam and updates the following QGraphicsView with QPixMaps. Additionally the QGraphicsView has a rubberband that is used to capture screenshots.

import logging
import os.path
import random
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QRect, QSize, QPoint, Qt
from PyQt5.QtGui import QMouseEvent, QPixmap, QImage, QRegion
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QRubberBand, QVBoxLayout, QLabel
import metaX.stylesheets as stylesheets

logger = logging.getLogger(__name__)


# TODO: maybe change the name of the module to video_output

def save_image(pixmap, photo_file_path):
    try:
        pixmap.save(photo_file_path)
    except OSError as e:
        logger.error(e)


class VideoContainerView(QGraphicsView):
    snapshot_taken = pyqtSignal(str)

    def __init__(self, mutex, wait_condition, config):
        super().__init__()
        self.mutex = mutex
        self.wait_condition = wait_condition
        self.config = config
        self.scene = self.set_scene()
        self.rubber_band = self.set_rubber_band()
        self.setMouseTracking(True)
        self._drawable = True
        self.set_ui()

    def set_scene(self):
        scene = QGraphicsScene()
        self.setScene(scene)
        return scene

    def set_rubber_band(self):
        rubber_band = QRubberBand(QRubberBand.Rectangle, self)
        # rubber_band.show()
        return rubber_band

    def set_ui(self):
        self.setStyleSheet("background-color: transparent;")


    def mouseMoveEvent(self, event: QMouseEvent) -> None:
        self.draw_rubber_band(event)

    def mousePressEvent(self, event: QMouseEvent) -> None:
        self.take_snapshot()

    def draw_rubber_band(self, event):
        self.rubber_band.setGeometry(
            QRect(QPoint(event.x() - self.config.crop_area / 2,
                         event.y() - self.config.crop_area / 2),
                  QSize(self.config.crop_area, self.config.crop_area)).normalized())

    def take_snapshot(self):
        if self.rubber_band.isVisible():
            self.rubber_band.hide()
            selected_area = self.rubber_band.geometry()
            pixmap = self.grab(selected_area)
            photo_file_path = f'{self.config.captures_path()}/{random.randint(1, 10000000)}.png'
            photo_file_path = os.path.abspath((photo_file_path))
            save_image(pixmap, photo_file_path)
            self.rubber_band.show()
            self.snapshot_taken.emit(photo_file_path)

    @pyqtSlot(QImage)
    def update_frame(self, current_frame):
        if self._drawable:
            self.mutex.lock()
            try:
                current_frame = current_frame
                pixmap = QPixmap.fromImage(current_frame)
                self.scene.clear()
                self.resetTransform()
                self.scene.addPixmap(pixmap)
                self.scene.update()
            finally:
                self.mutex.unlock()
                self.wait_condition.wakeAll()

    @pyqtSlot()
    def toggle_rubberband(self):
        if self.rubber_band.isVisible():
            self.rubber_band.hide()
        else:
            self.rubber_band.show()

    def reset_scene(self):
        print('reset_scene')
        self.scene.clear()
        self.scene.addPixmap(QPixmap())
        self.scene.update()

    @pyqtSlot()
    def set_drawable(self, drawable):
        self._drawable = drawable
        print(f'The VideoContainerView is drawable:{self._drawable}')

Now I would like to decorate the UI using custom shapes drawn on Figma.

Using this image for example: https://i.stack.imgur.com/qncxT.jpg, I would like the QGraphicsScene to be shown "under" the inner rounded rectange, i.e. the center of the rounded rectangle should align with the center of the shown QPixmap and only the part of the QPixmap that fits into the size of the inner rectangle should be shown, i.e. the inner rounded rectangle should act something like a mask.

I am not familiar with manually drawing mechanisms so any code snippet would be highly appreciated!

atomtm
  • 347
  • 1
  • 3
  • 9
  • If you use a QGrapichicsView, there is no need for a "background label". Then, you can use a top widget with the proper [`setMask()`](https://doc.qt.io/qt-5/qwidget.html#setMask). Besides, figma is certainly not a python module, so there's no way to insert it in a PyQt program, and absolutely not to do this. – musicamante May 23 '22 at 10:05
  • Appreciate your comment @musicamante. I was thinking to use the "background label" to enhance my UI with rich graphic elements (having shadows etc.). Also Figma is only used, solely to generate png/svg graphics, i.e. I did not intend to automatically bridge PyQt with it. And this is a general question I have. How do you decorate UI designs with custom png images and move away from default looking Qt applications – atomtm May 23 '22 at 12:16
  • That is too broad a question. There are many ways to change the appearance of Qt widgets, including stylesheets and custom painting. Note that Qt supports basic shadow effect (see [QGraphicsDropShadowEffect](//doc.qt.io/qt-5/qgraphicsdropshadoweffect.html)) that can also be applied to individual QGraphicsItems. If you want to draw a specific background, you can use the `background` or `background-image` of the QGraphicsView stylesheet, set a custom palette, or override the scene [`drawBackground()`](https://doc.qt.io/qt-5/qgraphicsscene.html#drawBackground). – musicamante May 23 '22 at 12:24
  • you're right ! To be more specific, I would like the QGraphicsView to be projected on the inner rounded rectangle : https://imgur.com/a/SeqNuyE. I would image to create a transparent hole in the inner rect, and overlay the whole image over the center of the QPixmap shown on the QGraphicsScene. – atomtm May 23 '22 at 12:41
  • Didn't you mention a "cloud" that would mask the contents? I suggest you to [edit] your question and show an image "mock-up" to clarify what is the expected result (possibly specifying/highlighting in another image what is part of what layer), as I suspect it could be simpler than you imagined. – musicamante May 23 '22 at 12:46
  • Thanks again @musicamante. I edited the question, hope its clearer now. – atomtm May 23 '22 at 14:02

0 Answers0