Reputation: 67
I am trying to draw over image using QPainter. It works good when using solid color. When using semi transparent color, dots were appearing.
Also when drawing multiple lines in one place, the color gets multiplied and produces a darker color.
import sys
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtGui import QPixmap, QPainter, QPen, QColor
class Menu(QMainWindow):
def __init__(self):
super().__init__()
self.drawing = False
self.lastPoint = QPoint()
self.image = QPixmap(r"C:\Users\www\Desktop\image.jpg")
self.setGeometry(100, 100, 500, 300)
self.resize(self.image.width(), self.image.height())
self.show()
def paintEvent(self, event):
painter = QPainter(self)
painter.drawPixmap(self.rect(), self.image)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drawing = True
self.lastPoint = event.pos()
def mouseMoveEvent(self, event):
if event.buttons() and Qt.LeftButton and self.drawing:
painter = QPainter(self.image)
painter.setPen(QPen(QColor(121,252,50,50), 20, Qt.SolidLine))
painter.drawLine(self.lastPoint, event.pos())
self.lastPoint = event.pos()
self.update()
def mouseReleaseEvent(self, event):
if event.button == Qt.LeftButton:
self.drawing = False
if __name__ == '__main__':
app = QApplication(sys.argv)
mainMenu = Menu()
sys.exit(app.exec_())
I need to keep the color as the original color(instead of getting darker each time) when drawing several times over the same place.
Upvotes: 0
Views: 2325
Reputation: 48260
Mouse movement are "discrete", which means that whenever you move your mouse you won't get continuous pixel coordinates: if you move your mouse fast enough from (0, 0)
to (20, 20)
, you'll probably get only two or three mouseMoveEvents in the middle at most, resulting in single segments for each mouse event.
The "dots" you see are actually areas where the different lines you draw collide, expecially since the mouse movement are not continuous. If you think of it as painting with watercolors, it's like if you draw a small line at each mouse movement, then wait until it's dried, then start to paint another line from the previous point.
As soon as you draw unique lines at each mouseMoveEvent, the edges of those segments are superimposed, resulting in those "less transparent dots" (since you're using a non opaque color), which are the points where the segments collide, and, because painting is usually "additive" you get two or more areas where the superimposed color results in a more opaque one: imagine it as watching through two pairs of sunglasses that are not aligned.
QPainterPath
, instead, can draw continuous lines without that "artifact", as long as they are part of the same painter path (no matter its subpath, including subpath polygons, ellipses, arcs, etc.). Then, whenever you tell the QPainter to draw a new element, it will be superimposed to the previous ones.
To better clarify, in this image on the left I'm drawing two distinct lines with a common vertex, using your color, which would be the case of a mousePressEvent (start drawing), a fast movement to the right (draw the first line) and another one to the bottom (draw another line). On the right there are the same "lines", but using a unique QPainterPath.
In this example code I temporarily create a painter path, which stores the current "drawing path" until the mouse is released, after which the path is actually applied to the QPixmap.
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtGui import QPixmap, QPainter, QPen, QColor, QPainterPath
class Menu(QWidget):
def __init__(self):
super().__init__()
self.drawingPath = None
self.image = QPixmap(r"testimage.jpg")
self.resize(self.image.width(), self.image.height())
self.show()
def paintEvent(self, event):
painter = QPainter(self)
painter.drawPixmap(self.rect(), self.image)
if self.drawingPath:
painter.setPen(QPen(QColor(121,252,50,50), 20, Qt.SolidLine))
painter.drawPath(self.drawingPath)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
# start a new QPainterPath and *move* to the current point
self.drawingPath = QPainterPath()
self.drawingPath.moveTo(event.pos())
def mouseMoveEvent(self, event):
if event.buttons() and Qt.LeftButton and self.drawingPath:
# add a line to the painter path, without "removing" the pen
self.drawingPath.lineTo(event.pos())
self.update()
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton and self.drawingPath:
# draw the painter path to the pixmap
painter = QPainter(self.image)
painter.setPen(QPen(QColor(121,252,50,50), 20, Qt.SolidLine))
painter.drawPath(self.drawingPath)
self.drawingPath = None
self.update()
if __name__ == '__main__':
app = QApplication(sys.argv)
mainMenu = Menu()
sys.exit(app.exec_())
There is only one problem with this: drawing over a currently drawing path won't result in a more opaque color, meaning that, as long as the mouse button is pressed, no matter how many times you "paint" over the same point, the color will always be the same. To get the "more opaque color" effect, you'll need to paint over the intersection(s), starting a new path each time.
PS: I used a QWidget, as in some cases a QMainWindow can grab mouse movements starting from a click on a non interactive area (like in this case) and use it to move the interface.
Upvotes: 4