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):
And here is what I want:
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()
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With