I'm trying to re-implement the scribble demo app for multiple layers of images. I am struggling to draw into the pixmap within the scene as the Painter complains that is being destroyed to early.
QPaintDevice: Cannot destroy paint device that is being painted
Could you please help me fix my code such that you can draw on to the roilayer pixmap with the red pen and that this layer starts transparent.
#!/usr/bin/python3
import sys
import os
from PyQt5.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem
from PyQt5.QtGui import QPixmap, QImage, QPainter, QPen
from PyQt5.QtCore import Qt, QRect
class Main(QGraphicsView):
def __init__(self):
super().__init__()
self.setWindowTitle("Scribble with layers")
self.scene = QGraphicsScene()
self.setScene(self.scene)
self.image = QImage('sample/test.bmp')
self.imlayer = QGraphicsPixmapItem(QPixmap.fromImage(self.image))
self.roilayer = QGraphicsPixmapItem(QPixmap(self.image.size()))
self.addlayer(self.imlayer)
self.addlayer(self.roilayer)
self.drawing = False
self.lastPoint = None
self.pencolour = Qt.red
self.penwidth = 2
self.show()
def addlayer(self, layer):
self.scene.addItem(layer)
self.updateviewer()
def updateviewer(self):
self.fitInView(self.sceneRect(), Qt.KeepAspectRatio)
def mousePressEvent(self, event):
# print(event.modifiers())
if event.button() == Qt.LeftButton:
self.lastPoint = event.pos()
self.drawing = True
def mouseMoveEvent(self, event):
if (event.buttons() & Qt.LeftButton) and self.drawing:
self.drawlineto(event.pos())
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton and self.drawing:
self.drawlineto(event.pos())
self.drawing = False
def drawlineto(self, position):
pixmap = self.roilayer.pixmap()
painter = QPainter(pixmap)
painter.setPen(QPen(self.pencolour, self.penwidth, Qt.SolidLine,
Qt.RoundCap, Qt.RoundJoin))
painter.drawLine(self.lastPoint, position)
self.imlayer.setPixmap(pixmap)
self.modified = True
rad = self.penwidth / 2 + 2
self.update(QRect(self.lastPoint, position).normalized().adjusted(-rad, -rad, +rad, +rad))
self.lastPoint = position
if __name__ == '__main__':
app = QApplication([])
main = Main()
sys.exit(app.exec_())
I had the same problem. The solution is to do the following to this part of your code:
def drawlineto(self, position):
pixmap = self.roilayer.pixmap()
painter = QPainter()
painter.begin(pixmap)
painter.setPen(QPen(self.pencolour, self.penwidth, Qt.SolidLine,
Qt.RoundCap, Qt.RoundJoin))
painter.drawLine(self.lastPoint, position)
painter.end()
self.imlayer.setPixmap(pixmap)
self.modified = True
Related
I am trying to build a paint app that has a grid, but I don't want to grid to be included in the saved photo.
I tried to draw on a transparent image, but I got a black background instead!
Here is the full code:
import sys
from PyQt5 import QtGui, QtWidgets, QtCore
from PyQt5.QtCore import QPoint, Qt
from PyQt5.QtGui import QBrush, QGuiApplication, QImage, QPainter, QPen, QIcon, QColor
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QTextEdit, QAction, QFileDialog,
QGraphicsScene, QGraphicsProxyWidget, QGraphicsView
import pyautogui
import matplotlib.pyplot as plt
class Drawer(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self._drawing = False
self.last_point = QPoint()
self.image_layer = QImage(self.size(), QImage.Format_RGB32)
self.image_layer.fill(Qt.gray)
self.brushSize = 2
self.brushColor = Qt.black
#paint = QPainter(self.image_layer)
#paint.setCompositionMode(QtGui.QPainter.CompositionMode_Clear)
# paint.drawLine(0,0,self.size().width(),0)
# paint.drawLine(0,10,200,10)
#paint.drawLine(0,0,0,200)
#paint.drawLine(10,0,10,200)
self.update()
def mousePressEvent(self, event):
self._drawing = True
self.last_point = event.pos()
def mouseMoveEvent(self, event):
if self._drawing and event.buttons() & Qt.LeftButton:
painter = QPainter(self.image_layer)
painter.setPen(
QPen(
self.brushColor,
self.brushSize,
Qt.SolidLine,
Qt.RoundCap,
Qt.RoundJoin,
)
)
painter.drawLine(self.last_point, event.pos())
self.last_point = event.pos()
self.update()
def paintEvent(self, event):
painter = QPainter(self)
painter.drawImage(QPoint(), self.image_layer)
painter.end()
def resizeEvent(self, event):
if (
self.size().width() > self.image_layer.width()
or self.size().height() > self.image_layer.height()
):
qimg = QImage(
max(self.size().width(), self.image_layer.width()),
max(self.size().height(), self.image_layer.height()),
QImage.Format_RGB32,
)
qimg.fill(Qt.gray)
painter = QPainter(qimg)
painter.drawImage(QPoint(), self.image_layer)
painter.drawLine(0, 0, qimg.size().width(), 0)
painter.drawLine(0, 10, qimg.size().width(), 10)
painter.drawLine(0, 600, qimg.size().width(), 600)
print(qimg.size().height())
painter.end()
self.image_layer = qimg
self.update()
class Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
[x, y] = pyautogui.size()
self.setGeometry(0, 0, x, y)
self.drawer = Drawer()
textbox = QTextEdit("Converted text will show here")
central_widget = QWidget()
self.setCentralWidget(central_widget)
vlay = QVBoxLayout(central_widget)
vlay.addWidget(textbox)
vlay.addWidget(self.drawer, stretch=1)
mainMenu = self.menuBar()
fileMenu = mainMenu.addMenu("File")
saveAction = QAction(QIcon("icons/save.png"), "Save", self)
saveAction.setShortcut("Ctrl+S")
fileMenu.addAction(saveAction)
saveAction.triggered.connect(self.save)
def save(self):
filePath, _ = QFileDialog.getSaveFileName(self.drawer, "Save Image", "",
"PNG(*.png);;JPEG(*.jpg *.jpeg);;All Files(*.*) ")
if filePath == "":
return
self.drawer.image_layer.save(filePath)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())
Note: there are only two lines that mimics the grid, I can draw it later, but for now I want the saved image to not include the grind lines.
You should paint the grid in the paintEvent, instead of continuously painting it onto the image.
def paintEvent(self, event):
painter = QPainter(self)
painter.drawImage(QPoint(), self.image_layer)
gridSize = 10
x = y = 0
width = self.width()
height = self.height()
while y <= height:
# draw horizontal lines
painter.drawLine(0, y, width, y)
y += gridSize
while x <= width:
# draw vertical lines
painter.drawLine(x, 0, x, height)
x += gridSize
I can add QWidget to QMainWindow and set its position to cursor position.
But every single time, I want to add new QWidget to QMainWindow. I don't maybe given codes add new QWidget but at canvas, I see just one QWidget. Here my codes:
from PyQt5.QtWidgets import QMainWindow, QApplication, QMenu, QMenuBar, QAction, QFileDialog, QWidget, QLabel
from PyQt5.QtGui import QIcon, QImage, QPainter, QPen, QBrush
from PyQt5.QtCore import Qt, QPoint
import sys
class Window(QMainWindow):
def __init__(self):
super().__init__()
title = "Paint Application"
top = 400
left = 400
width = 800
height = 600
self.objStr = "berkayy"
self.count = 0
# icon = "icons/pain.png"
self.setAcceptDrops(True)
self.setWindowTitle(title)
self.setGeometry(top, left, width, height)
# self.setWindowIcon(QIcon(icon))
self.image = QImage(self.size(), QImage.Format_RGB32)
self.image.fill(Qt.white)
self.drawing = False
self.brushSize = 2
self.brushColor = Qt.black
self.lastPoint = QPoint()
mainMenu = self.menuBar()
fileMenu = mainMenu.addMenu("File")
brushSize = mainMenu.addMenu("Brush Size")
brushColor = mainMenu.addMenu("Brush Color")
saveAction = QAction(QIcon("icons/save.png"), "Save",self)
saveAction.setShortcut("Ctrl+S")
fileMenu.addAction(saveAction)
saveAction.triggered.connect(self.save)
clearAction = QAction(QIcon("icons/clear.png"), "Clear", self)
clearAction.setShortcut("Ctrl+C")
fileMenu.addAction(clearAction)
clearAction.triggered.connect(self.clear)
threepxAction = QAction( QIcon("icons/threepx.png"), "3px", self)
brushSize.addAction(threepxAction)
threepxAction.triggered.connect(self.threePixel)
fivepxAction = QAction(QIcon("icons/fivepx.png"), "5px", self)
brushSize.addAction(fivepxAction)
fivepxAction.triggered.connect(self.fivePixel)
sevenpxAction = QAction(QIcon("icons/sevenpx.png"),"7px", self)
brushSize.addAction(sevenpxAction)
sevenpxAction.triggered.connect(self.sevenPixel)
ninepxAction = QAction(QIcon("icons/ninepx.png"), "9px", self)
brushSize.addAction(ninepxAction)
ninepxAction.triggered.connect(self.ninePixel)
blackAction = QAction(QIcon("icons/black.png"), "Black", self)
blackAction.setShortcut("Ctrl+B")
brushColor.addAction(blackAction)
blackAction.triggered.connect(self.blackColor)
whitekAction = QAction(QIcon("icons/white.png"), "White", self)
whitekAction.setShortcut("Ctrl+W")
brushColor.addAction(whitekAction)
whitekAction.triggered.connect(self.whiteColor)
redAction = QAction(QIcon("icons/red.png"), "Red", self)
redAction.setShortcut("Ctrl+R")
brushColor.addAction(redAction)
redAction.triggered.connect(self.redColor)
greenAction = QAction(QIcon("icons/green.png"), "Green", self)
greenAction.setShortcut("Ctrl+G")
brushColor.addAction(greenAction)
greenAction.triggered.connect(self.greenColor)
yellowAction = QAction(QIcon("icons/yellow.png"), "Yellow", self)
yellowAction.setShortcut("Ctrl+Y")
brushColor.addAction(yellowAction)
yellowAction.triggered.connect(self.yellowColor)
def rectangle(self, e, pos):
print(pos)
painter = QPainter(self)
painter.setPen(QPen(Qt.black, 5, Qt.SolidLine))
painter.setBrush(QBrush(Qt.green, Qt.DiagCrossPattern))
painter.drawRect(100, 15, 400, 200)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drawing = True
self.lastPoint = event.pos()
#print(self.lastPoint)
def contextMenuEvent(self, event):
contextMenu = QMenu(self)
newAct = contextMenu.addAction("New")
openAct = contextMenu.addAction("Open")
closeAct = contextMenu.addAction("Close")
action = contextMenu.exec_(self.mapToGlobal(event.pos()))
if action == closeAct:
self.close()
elif action == openAct:
self.berkay(event.pos())
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()
def berkay(self, pos):
wid = QWidget(self)
btn = QLabel(wid)
btn.setText("skjdf")
btn.setObjectName(self.objStr + str(self.count) )
btn.move(pos)
self.setCentralWidget(wid)
self.count += 1
# btn.setDragEnabled(True)
print(btn.objectName())
# self.show()
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.drawing = False
def paintEvent(self, event):
canvasPainter = QPainter(self)
canvasPainter.drawImage(self.rect(),self.image, self.image.rect() )
def save(self):
filePath, _ = QFileDialog.getSaveFileName(self, "Save Image", "", "PNG(*.png);;JPEG(*.jpg *.jpeg);;All Files(*.*) ")
if filePath == "":
return
self.image.save(filePath)
def clear(self):
self.image.fill(Qt.white)
self.update()
def threePixel(self):
self.brushSize = 3
def fivePixel(self):
self.brushSize = 5
def sevenPixel(self):
self.brushSize = 7
def ninePixel(self):
self.brushSize = 9
def blackColor(self):
self.brushColor = Qt.black
def whiteColor(self):
self.brushColor = Qt.white
def redColor(self):
self.brushColor = Qt.red
def greenColor(self):
self.brushColor = Qt.green
def yellowColor(self):
self.brushColor = Qt.yellow
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
app.exec()
I want to create new QWidget when user clicked context menu item every single time.
A QMainWindow can have only one central widget. So, adding the new QLabel as central widget will remove the previous. That's why you can see only the last label.
Create a single widget and define it as central widget. Then, add the label as child of the central widget:
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.area = QWidget(self)
self.setCentralWidget(self.area)
def berkay(self, pos):
btn = QLabel("BOOH", self.area)
btn.move(self.area.mapFromParent(pos)) # Map the pos in the coord system of self.area
btn.show()
First, for every single widget, store x and y value in a list. After that, adding new and current widgets to window central widget.
Edit
We do not need store widget x and y, anymore.
Thank you Romha for optimisation suggestion.
import sys
from PyQt5.QtWidgets import QMainWindow, QMenu, QApplication, QWidget, QPushButton, qApp
from PyQt5 import QtGui, QtCore
from PyQt5.QtCore import Qt
class Example(QMainWindow):
def __init__(self):
super().__init__()
self.area = QWidget(self)
self.setCentralWidget(self.area)
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('Context menu')
self.setStyleSheet("QMainWindow {background: 'white';}")
self.show()
def contextMenuEvent(self, event):
cmenu = QMenu(self)
addBtnAct = cmenu.addAction("Add Button")
quitAct = cmenu.addAction("Quit")
action = cmenu.exec_(self.mapToGlobal(event.pos()))
if action == quitAct:
qApp.quit()
elif action == addBtnAct:
self.addLabel(event.pos())
def addLabel(self, pos):
btn = QLabel("BOOH", self.area)
btn.move(self.area.mapFromParent(pos)) # Map the pos in the coord system of self.area
btn.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
I'm pretty new to PyQt and am trying to make an application with a QPixmap on the left, which can be drawn on, and a QTextEdit on the right (for a simple OCR GUI).
I looked at:
PyQt5 Image and QGridlayout
but I couldn't connect it with the code below (I'm losing my hair with all the head scratching!!)
When I try adapting the following code, what I get is a QMainWindow with the QPixmap as the background which can be drawn on with the mouse and a second occurance of the QPixmap in it's correct position, which can not be drawn on. Can someone tell me what I'm doing wrong?
Thank you very much!
# https://stackoverflow.com/questions/51475306/
import sys
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtWidgets import QMainWindow, QApplication,QGridLayout, QLabel, QWidget, QTextEdit
from PyQt5.QtGui import QPixmap, QPainter, QPen
class Menu(QMainWindow):
def __init__(self):
super().__init__()
self.drawing = False
self.lastPoint = QPoint()
self.image = QPixmap("S3.png")
self.setGeometry(100, 100, 500, 300)
self.resize(self.image.width(), self.image.height())
layout = QGridLayout()
# Add a QTextEdit box
self.edit = QTextEdit()
layout.addWidget(self.edit, 0, 0, 10, 20)
# This:
# https://stackoverflow.com/questions/52616553
# indicates that a QPixmap must be put into a label to insert into a QGridLayout
self.label = QLabel()
self.label.setPixmap(self.image)
layout.addWidget(self.label, 10, 20, 10, 20)
# https://stackoverflow.com/questions/37304684/
self.widget = QWidget()
self.widget.setLayout(layout)
self.setCentralWidget(self.widget)
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()
print(self.lastPoint)
def mouseMoveEvent(self, event):
if event.buttons() and Qt.LeftButton and self.drawing:
painter = QPainter(self.image)
painter.setPen(QPen(Qt.red, 3, Qt.SolidLine))
painter.drawLine(self.lastPoint, event.pos())
print(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_())
Each widget must fulfill a specific task, so I have created a widget that only has the painted function, the main widget works as a container for the painting widget and the QTextEdit.
from PyQt5 import QtCore, QtGui, QtWidgets
class Label(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Label, self).__init__(parent)
self.image = QtGui.QPixmap("S3.png")
self.drawing = False
self.lastPoint = QtCore.QPoint()
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.drawPixmap(QtCore.QPoint(), self.image)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.drawing = True
self.lastPoint = event.pos()
def mouseMoveEvent(self, event):
if event.buttons() and QtCore.Qt.LeftButton and self.drawing:
painter = QtGui.QPainter(self.image)
painter.setPen(QtGui.QPen(QtCore.Qt.red, 3, QtCore.Qt.SolidLine))
painter.drawLine(self.lastPoint, event.pos())
self.lastPoint = event.pos()
self.update()
def mouseReleaseEvent(self, event):
if event.button == QtCore.Qt.LeftButton:
self.drawing = False
def sizeHint(self):
return self.image.size()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.label = Label()
self.textedit = QtWidgets.QTextEdit()
widget = QtWidgets.QWidget()
self.setCentralWidget(widget)
lay = QtWidgets.QHBoxLayout(widget)
lay.addWidget(self.label, alignment=QtCore.Qt.AlignCenter)
lay.addWidget(self.textedit)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I have a functioning drawing application for some segmentation on images. For this I have two layers, the original image and the image layer I am drawing on.
I now want to implement a method for erasing. I have implemented undo functionality, but I would also like the user to be able to select a brush "color" as to be able to erase specific parts, like the eraser in paint. I thought this would be possible by drawing with a color with opacity, but that just results in no line being drawn.
The goal for me is therefore to draw a line, that removes the pixel values in the image layer, such that I can see the underlying image
MVP of drawing
from PyQt5.QtWidgets import QApplication, QMainWindow, QMenuBar, QMenu, QAction
from PyQt5.QtGui import QIcon, QImage, QPainter, QPen
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtGui import QColor
import sys
class Window(QMainWindow):
def __init__(self):
super().__init__()
top = 400
left = 400
width = 800
height = 600
self.setWindowTitle("MyPainter")
self.setGeometry(top, left, width, height)
self.image = QImage(self.size(), QImage.Format_ARGB32)
self.image.fill(Qt.white)
self.imageDraw = QImage(self.size(), QImage.Format_ARGB32)
self.imageDraw.fill(Qt.transparent)
self.drawing = False
self.brushSize = 2
self.brushColor = Qt.black
self.lastPoint = QPoint()
self.change = False
mainMenu = self.menuBar()
changeColour = mainMenu.addMenu("changeColour")
changeColourAction = QAction("change",self)
changeColour.addAction(changeColourAction)
changeColourAction.triggered.connect(self.changeColour)
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.imageDraw)
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()
def mouseReleaseEvent(self, event):
if event.button == Qt.LeftButton:
self.drawing = False
def paintEvent(self, event):
canvasPainter = QPainter(self)
canvasPainter.drawImage(self.rect(), self.image, self.image.rect())
canvasPainter.drawImage(self.rect(), self.imageDraw, self.imageDraw.rect())
def changeColour(self):
if not self.change:
# erase
self.brushColor = QColor(255,255,255,0)
else:
self.brushColor = Qt.black
self.change = not self.change
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
app.exec()
How to erase a subset of pixels?
As in this example what color should be given to self.brushColor in the changeColour function?
Info
The colour white is not the solution, because in reality the image at the bottom is a complex image, I therefore want to make the toplayer "see-through" again, when erasing.
You have to change the compositionMode to QPainter::CompositionMode_Clear and erase with eraseRect().
from PyQt5 import QtCore, QtGui, QtWidgets
class Window(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
top, left, width, height = 400, 400, 800, 600
self.setWindowTitle("MyPainter")
self.setGeometry(top, left, width, height)
self.image = QtGui.QImage(self.size(), QtGui.QImage.Format_ARGB32)
self.image.fill(QtCore.Qt.white)
self.imageDraw = QtGui.QImage(self.size(), QtGui.QImage.Format_ARGB32)
self.imageDraw.fill(QtCore.Qt.transparent)
self.drawing = False
self.brushSize = 2
self._clear_size = 20
self.brushColor = QtGui.QColor(QtCore.Qt.black)
self.lastPoint = QtCore.QPoint()
self.change = False
mainMenu = self.menuBar()
changeColour = mainMenu.addMenu("changeColour")
changeColourAction = QtWidgets.QAction("change", self)
changeColour.addAction(changeColourAction)
changeColourAction.triggered.connect(self.changeColour)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.drawing = True
self.lastPoint = event.pos()
def mouseMoveEvent(self, event):
if event.buttons() and QtCore.Qt.LeftButton and self.drawing:
painter = QtGui.QPainter(self.imageDraw)
painter.setPen(QtGui.QPen(self.brushColor, self.brushSize, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin))
if self.change:
r = QtCore.QRect(QtCore.QPoint(), self._clear_size*QtCore.QSize())
r.moveCenter(event.pos())
painter.save()
painter.setCompositionMode(QtGui.QPainter.CompositionMode_Clear)
painter.eraseRect(r)
painter.restore()
else:
painter.drawLine(self.lastPoint, event.pos())
painter.end()
self.lastPoint = event.pos()
self.update()
def mouseReleaseEvent(self, event):
if event.button == QtCore.Qt.LeftButton:
self.drawing = False
def paintEvent(self, event):
canvasPainter = QtGui.QPainter(self)
canvasPainter.drawImage(self.rect(), self.image, self.image.rect())
canvasPainter.drawImage(self.rect(), self.imageDraw, self.imageDraw.rect())
def changeColour(self):
self.change = not self.change
if self.change:
pixmap = QtGui.QPixmap(QtCore.QSize(1, 1)*self._clear_size)
pixmap.fill(QtCore.Qt.transparent)
painter = QtGui.QPainter(pixmap)
painter.setPen(QtGui.QPen(QtCore.Qt.black, 2))
painter.drawRect(pixmap.rect())
painter.end()
cursor = QtGui.QCursor(pixmap)
QtWidgets.QApplication.setOverrideCursor(cursor)
else:
QtWidgets.QApplication.restoreOverrideCursor()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())
I'm trying to draw a rectangle on GUI created by PyQt5 by drag and drop. I managed to do that, but the rectangle is drawn when the mouse left key is released.
What I want to do is like this link:
When the mouse left button is pressed, start drawing the rectangle.
While dragging, adjust the rectangle shape with the mouse movement.
When the mouse left button is released, determine the rectangle shape.
How can I implement this? Thanks in advance.
Here's my code.
# -*- coding: utf-8 -*-
import sys
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtGui import QPainter
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setGeometry(30,30,600,400)
self.pos1 = [0,0]
self.pos2 = [0,0]
self.show()
def paintEvent(self, event):
width = self.pos2[0]-self.pos1[0]
height = self.pos2[1] - self.pos1[1]
qp = QPainter()
qp.begin(self)
qp.drawRect(self.pos1[0], self.pos1[1], width, height)
qp.end()
def mousePressEvent(self, event):
self.pos1[0], self.pos1[1] = event.pos().x(), event.pos().y()
print("clicked")
def mouseReleaseEvent(self, event):
self.pos2[0], self.pos2[1] = event.pos().x(), event.pos().y()
print("released")
self.update()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MyWidget()
window.show()
app.aboutToQuit.connect(app.deleteLater)
sys.exit(app.exec_())
You do not have to use the mouseReleaseEvent function, but the mouseMoveEvent function that is called each time the mouse is moved, and I have modified the code to make it simpler.
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setGeometry(30,30,600,400)
self.begin = QtCore.QPoint()
self.end = QtCore.QPoint()
self.show()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
br = QtGui.QBrush(QtGui.QColor(100, 10, 10, 40))
qp.setBrush(br)
qp.drawRect(QtCore.QRect(self.begin, self.end))
def mousePressEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
self.update()
def mouseMoveEvent(self, event):
self.end = event.pos()
self.update()
def mouseReleaseEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
self.update()