Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making a fullscreen paint program with transparent background of my application

My goal is to make a little PC/Windows program which will allow me to literally draw on top of my screen and save the result as png with transparent background. Like softwares like Epic Pen or gInk but my way. All using Python 3.7 and PyQt5.

So far, I managed to get a functional drawing app (basically following this tutorial) because I'm learning PyQt at the same time. I managed to save my drafts as png with transparent background. I can make the drawing board fullscreen and borderless.

Now the issue is, I can't find a way to make the whole background transparent. Though I've found ways to make a window transparent and borderless using these:

Window = Window()
Window.setStyleSheet("background:transparent;")
Window.setAttribute(Qt.WA_TranslucentBackground)
Window.setWindowFlags(Qt.FramelessWindowHint)
Window.show()

And it works... Until I have a drawing area. I can draw on it, it will save with a transparent background, but it displays black.

So I'm searching for that solution. Even without PyQt, I don't really care, as long as I can make my program work.

So here is what I have (I show you windowed with the frame to make it easier to explain): enter image description here

And here is what I want: enter image description here

And here is my code:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QMenuBar, QMenu, QAction, QShortcut, QFileDialog
from PyQt5.QtGui import QIcon, QImage, QPainter, QPen
from PyQt5.QtCore import Qt, QPoint


class Window(QMainWindow):
    def __init__(self):
        super().__init__()

        top = 400
        left = 400
        width = 800
        height = 600

        icon = "icons/icon.png"

        self.setWindowTitle("ScreenPen drawing board")
        self.setGeometry(top, left, width, height)
        self.setWindowIcon(QIcon(icon))

# ---------- sets image ----------
        self.image = QImage(self.size(), QImage.Format_RGBA64)
        self.image.fill(Qt.transparent)

# ---------- init drawing state ----------
        self.drawing = False
        self.brushSize = 2
        self.brushColor = Qt.red
        self.lastPoint = QPoint()

# ---------- Define Menus ----------
    # mainmenu
        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu("File")
        toolMenu = mainMenu.addMenu("Tool")
        toolColor = mainMenu.addMenu("Color")
    # smenu save
        saveAction = QAction(QIcon("icons/save.png"), "Save", self)
        saveAction.setShortcut("Ctrl+S")
        fileMenu.addAction(saveAction)
        saveAction.triggered.connect(self.saveFrame)
    # smenu clear frame
        clearFrameAction = QAction(QIcon("icons/clear.png"), "Clear Frame", self)
        clearFrameAction.setShortcut("Ctrl+Del")
        fileMenu.addAction(clearFrameAction)
        clearFrameAction.triggered.connect(self.clearFrame)
    # smenu Tool Pen
        toolPenAction = QAction(QIcon("icons/toolPen.png"), "Pen", self)
        # clearAction.setShortcut("Ctrl+Del")
        toolMenu.addAction(toolPenAction)

# ---------- Catch Mouse Down --------

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drawing = True
            self.lastPoint = event.pos()

# ---------- Catch Mouse Move --------
    def mouseMoveEvent(self, event):
        if (event.buttons() & Qt.LeftButton) & self.drawing:
            painter = QPainter(self.image)
            painter.setPen(QPen(self.brushColor, self.brushSize, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
            painter.drawLine(self.lastPoint, event.pos())
            self.lastPoint = event.pos()
            self.update()

# ---------- Catch Mouse Up --------
    def mouseReleaseEvent(self, event):
        if event.button == Qt.LeftButton:
            self.drawing = False

# ---------- Paint --------
    def paintEvent(self, event):
        canvasPainter = QPainter(self)
        canvasPainter.drawImage(self.rect(), self.image, self.image.rect())

# ---------- Save Action ----------
    def saveFrame(self):
        filePath,  _ = QFileDialog.getSaveFileName(self, "Save Image", "", "PNG(*.png);;JPEG(*.jpg *.jpeg);; ALL Files(*.*)")
        if filePath == "":
            return
        self.image.save(filePath)

# ---------- Clear Frame Action ----------
    def clearFrame(self):
        self.image.fill(Qt.white)
        self.update()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    Window = Window()
    # Window style
    Window.setStyleSheet("background:transparent;")
    Window.setAttribute(Qt.WA_TranslucentBackground)
    # Window.setWindowFlags(Qt.FramelessWindowHint)
    Window.show()
    app.exec()
like image 432
L0Lock Avatar asked Sep 29 '19 16:09

L0Lock


1 Answers

One way to do this (which should work on most platforms) is to create an image of the whole desktop that is then cropped to the area covered by your window. This can be done quite easily in Qt using QScreen.grabWindow:

def saveFrame(self):
    filePath,  _ = QFileDialog.getSaveFileName(self, "Save Image", "", "PNG(*.png);;JPEG(*.jpg *.jpeg);; ALL Files(*.*)")
    if filePath == "":
        return

    screen = QApplication.desktop().windowHandle().screen()
    wid = QApplication.desktop().winId()
    pixmap = screen.grabWindow(wid, self.x(), self.y(), self.width(), self.height())
    pixmap.save(filePath)

Or possibly:

    screen = self.windowHandle().screen()
    pixmap = screen.grabWindow(0, self.x(), self.y(), self.width(), self.height())
    pixmap.save(filePath)

These both work for me on Linux, but I have not tested them on other platforms. If you want to get the window frame as well, use self.frameGeometry() to get the required dimensions.

like image 65
ekhumoro Avatar answered Sep 18 '22 14:09

ekhumoro