draw on the lower part of the window PyQt5 - python

I am trying to make a program the paints on the half button of the screen only, but as you can see the painting is shifted towards the bottom, if I draw in the upper part, the paint happens in the lower part.
What I want is to draw directly in the bottom part.
here is my code:
from PIL.ImageEnhance import Color
from PyQt5.QtWidgets import QMainWindow, QApplication, QMenu, QMenuBar, QAction, QFileDialog, QTextEdit, QVBoxLayout, \
QWidget, QLabel
from PyQt5.QtGui import QIcon, QImage, QPainter, QPen, QBrush, QPixmap
from PyQt5.QtCore import Qt, QPoint, QSize, QRect
import sys
import pyautogui
class Window(QMainWindow):
def __init__(self):
super().__init__()
title = "Digital Waraq"
icon = "icons/pain.png"
[x, y] = pyautogui.size()
self.setWindowTitle(title)
self.setGeometry(0, 0, x, y)
self.setWindowIcon(QIcon(icon))
self.image = QImage(pyautogui.size().width, int(pyautogui.size().height/2), QImage.Format_RGB32)
self.image.fill(Qt.gray)
self.drawing = False
self.brushSize = 2
self.brushColor = Qt.black
self.lastPoint = QPoint()
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() & 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 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())
newRect = QRect(QPoint(0, int(pyautogui.size().height/2)), QSize(self.image.size()))
#canvasPainter.drawImage(newRect, self.image, self.image.rect())
canvasPainter.drawImage(newRect, self.image, self.image.rect())
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())

Do not complicate the task dividing the painting area within the widget, a simpler solution is to create a widget where the painting is done completely and then place it at the bottom of the main window.
import sys
from PyQt5.QtCore import QPoint, Qt
from PyQt5.QtGui import QBrush, QGuiApplication, QImage, QPainter, QPen
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
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
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 mouseReleaseEvent(self, event):
self._drawing = True
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.end()
self._image_layer = qimg
self.update()
class Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.drawer = Drawer()
central_widget = QWidget()
self.setCentralWidget(central_widget)
vlay = QVBoxLayout(central_widget)
vlay.setContentsMargins(0, 0, 0, 0)
vlay.addStretch(1)
vlay.addWidget(self.drawer, stretch=1)
r = QGuiApplication.primaryScreen().availableGeometry()
self.setGeometry(r)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())

Related

How can I add new ellipse while retaining the previous ellipse?

Currently, my code can paint an ellipse but every time I click the mouse for a new ellipse the previous ellipse will not retain .
I am used the **QGraphicsView** Framework because I am using its pantool and zoom function but I have a hard time adding the paint function.
import sys
from PyQt5.QtCore import Qt, QRectF, QPointF
from PyQt5.QtGui import QPixmap, QTransform, QBrush, QColor, QPen, QPainterPath
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QGraphicsView,
QGraphicsScene, QGraphicsPixmapItem, QSizePolicy, QSpacerItem, QGraphicsObject
class MouseBrushObject(QGraphicsObject):
def __init__(self):
QGraphicsObject.__init__(self)
self._size = 10
self._x = 0
self._y = 0
self._pen = None
self._brush = None
self._color = None
self.setColor(QColor(255, 0, 0, 255))
def paint(self, painter, option, widget):
rect = self.boundingRect()
painter.setPen(self._pen)
painter.setBrush(self._brush)
painter.drawEllipse(QPointF(self._x,self._y), 5,5)
def boundingRect(self):
return QRectF(self._x, self._y, self._size, self._size)
def setColor(self, color):
self._color = color
self._pen = QPen(self._color, 1)
self._brush = QBrush(QColor(self._color.red(), self._color.green(), self._color.blue(), 40))
def setSize(self, size):
self._size = size
def setPosition(self, pos):
self._x = pos.x() - pos.x()/2
self._y = pos.y() - pos.y()/2
self.setPos(QPointF(self._x, self._y))
class View(QGraphicsView):
def __init__(self, parent=None):
QGraphicsView.__init__(self, parent=parent)
self.setMouseTracking(True)
self.scene = QGraphicsScene(self)
self.setScene(self.scene)
pixmap = QPixmap(300, 300)
self.scene.addItem(QGraphicsPixmapItem(pixmap))
#self.setTransform(QTransform().scale(1, 1).rotate(0))
self.scene.setBackgroundBrush(QBrush(Qt.lightGray))
self._brushItem = MouseBrushObject()
self.path = QPainterPath()
def mousePressEvent(self, event):
pos = event.pos()
self._brushItem.setPosition(pos)
def enterEvent(self, event):
self.scene.addItem(self._brushItem)
return super(View, self).enterEvent(event)
def leaveEvent(self, event):
self.scene.removeItem(self._brushItem)
return super(View, self).leaveEvent(event)
class Viewer(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent=parent)
layout = QVBoxLayout()
self.view = View(self)
self.setLayout(layout)
layout.addWidget(self.view)
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.viewer = Viewer(self)
layout = QVBoxLayout()
layout.addWidget(self.viewer)
centralwidget = QWidget(self)
centralwidget.setLayout(layout)
self.setCentralWidget(centralwidget)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
Every time the mousePressEvent is trigger, it will paint an ellipse but the problem is that it will not retain the previous ellipse will adding the new ellipse.
My goal is for this code is two paint an ellipse when the mouse is clicked as it will serve as a point. Then The two points will be automatically connect using path. I have tried it will the qpainter function and it works but since I can't used qpaintEvent in QgraphicsView, I have a hard time executing the code.

How to draw polyline with PyQt5 in Python?

I want to draw polyline with mouse event. But I can't set endpoints by clicking, or choose pen type. I want to draw linear lines, but when i write this code it only shows dots instead of drawing a line. Here is my code:
import sys
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtGui import QPainter, QBrush, QColor, QPen, QPainterPath
from PyQt5.QtWidgets import QLabel, QGraphicsScene, QGraphicsView
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.begin = QtCore.QPoint()
self.end = QtCore.QPoint()
self.beginList = []
self.endList = []
self.initUI()
def initUI(self):
self.setGeometry(200, 200, 1000, 500)
self.label = QLabel(self)
self.label.resize(500, 40)
self.show()
def paintEvent(self, event):
qp = QPainter(self)
for i,j in zip(self.beginList, self.endList):
qp.drawLines(QtCore.QLineF(i,j))
def mouseMoveEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
self.beginList.append(self.begin)
self.endList.append(self.end)
self.label.setText('Coordinates: ( %d : %d )' % (event.x(), event.y()))
self.update()
def mouseReleaseEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MyWidget()
window.resize(800,600)
sys.exit(app.exec_())
If the OP code is analyzed, the starting point and the end point coincide, so when drawing a line between 2 points of the same location, only one point will be drawn. The logic is to join the point obtained in the i-th step with the (i+1)-th point.
To do the above the simplest thing is to use a QPainterPath:
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.paths = []
def initUI(self):
self.setGeometry(200, 200, 1000, 500)
self.label = QtWidgets.QLabel(self)
self.show()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
for path in self.paths:
qp.drawPath(path)
def mousePressEvent(self, event):
path = QtGui.QPainterPath()
path.moveTo(event.pos())
self.paths.append(path)
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
self.paths[-1].lineTo(event.pos())
self.label.setText('Coordinates: ( %d : %d )' % (event.x(), event.y()))
self.label.adjustSize()
self.update()
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self.paths[-1].lineTo(event.pos())
self.update()
super().mouseReleaseEvent(event)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MyWidget()
window.resize(800, 600)
sys.exit(app.exec_())

PyQt5: drawing multiple rectangles using events

I am creating a desktop application using PyQt5 where the user will be able to draw rectangles.
User should be able to select the top-left corner of the rectangle with first mouse click and the bottom-right corner with second mouse click. A rectangle should appear in that location with the perimeter well defined. I created application but have a problem when I draw another rectangle previous rectangle vanishes. I am not able to draw multiple rectangles.
Please find the below code for reference
import sys
from PyQt5.QtWidgets import *
from PyQt5 import QtGui, QtCore
from PyQt5.QtGui import QPainter, QPen, QBrush
from PyQt5.QtCore import Qt
class Windo(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(150,250,500,500)
self.setWindowTitle("Ammyyy")
self.setWindowIcon(QtGui.QIcon('a.jpeg'))
self.begin = QtCore.QPoint()
self.end = QtCore.QPoint()
self.show()
def paintEvent(self,event):
qp = QPainter(self)
qp.begin(self)
qp.setPen(QPen(Qt.black, 6, Qt.SolidLine))
qp.drawRect(QtCore.QRect(self.begin, self.end))
qp.end()
def mousePressEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
def mouseMoveEvent(self, event):
self.end = event.pos()
self.update()
def mouseReleaseEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
app = QApplication(sys.argv)
win = Windo()
sys.exit(app.exec_())
If you want to draw n-rectangles then you must save that information in a list through QRect. On the other hand, the selection of 2 points does not imply that the QRect is valid, for example if the first point is on the right, the second point will not create a valid rectangle so that rectangle has to be normalized. Considering the above, the solution is:
import sys
from PyQt5.QtCore import Qt, QPoint, QRect
from PyQt5.QtGui import QPainter, QPen, QBrush, QIcon
from PyQt5.QtWidgets import QApplication, QWidget
class Window(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(150, 250, 500, 500)
self.setWindowTitle("Ammyyy")
self.setWindowIcon(QIcon("a.jpeg"))
self.begin = QPoint()
self.end = QPoint()
self.rectangles = []
def paintEvent(self, event):
qp = QPainter(self)
qp.setPen(QPen(Qt.black, 6, Qt.SolidLine))
for rectangle in self.rectangles:
qp.drawRect(rectangle)
if not self.begin.isNull() and not self.end.isNull():
qp.drawRect(QRect(self.begin, self.end).normalized())
def mousePressEvent(self, event):
self.begin = self.end = event.pos()
self.update()
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
self.end = event.pos()
self.update()
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
r = QRect(self.begin, self.end).normalized()
self.rectangles.append(r)
self.begin = self.end = QPoint()
self.update()
super().mouseReleaseEvent(event)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())

Drawing on top of image in PyQt5 tracing the mouse

I'm new to PyQt5. I'm trying to draw on top of a loaded image lines that are tracing my mouse, i.e. pressing the mouse will result in drawing whatever your mouse moves to, while the mouse is still pressed, and stops when we release the mouse.
I saw This answer and it was very helpful, but I'm trying to do something a bit more complicated.
I think I'm messing up with the events.
What I wrote (which doesn't work):
import sys
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtWidgets import QMainWindow, QApplication, QLabel
from PyQt5.QtGui import QPixmap, QPainter, QPen
class Menu(QMainWindow):
def __init__(self):
super().__init__()
self.drawing = False
self.lastPoint = QPoint()
self.image = QPixmap("picture.png")
self.setGeometry(100, 100, 500, 300)
self.resize(self.image.width(), self.image.height())
self.label = QLabel(self)
self.show()
def paintEvent(self, event):
painter = QPainter(self)
painter.drawPixmap(self.rect(), self.image)
pen = QPen(Qt.red, 3)
painter.setPen(pen)
painter.drawLine(self.lastPoint, event.pos())
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(Qt.red, 3, Qt.SolidLine))
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_())
You want to use drawLine in mouseMoveEvent. When you move your mouse, it will invoke mouseMoveEvent which will draw a line from the last position, and then it will invoke by itself the paintEvent.
Try this:
import sys
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtGui import QPixmap, QPainter, QPen
class Menu(QMainWindow):
def __init__(self):
super().__init__()
self.drawing = False
self.lastPoint = QPoint()
self.image = QPixmap("picture.png")
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(Qt.red, 3, 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_())

Drag and Drop QLabels with PyQt5

I'm trying to drag and drop a Qlabel on another Qlabel with PyQt5:
from PyQt5.QtWidgets import QApplication, QWidget, QToolTip, QPushButton, QMessageBox, QHBoxLayout, QVBoxLayout, QGridLayout,QFrame, QComboBox, QLabel, QLineEdit
from PyQt5.QtGui import QIcon, QFont, QPixmap, QImage
import sys
class my_label(QLabel):
def __init__(self,title,parent):
super().__init__(title,parent)
self.setAcceptDrops(True)
def dragEnterEvent(self,event):
if event.mimeData().hasFormat("text/plain"):
event.accept()
else:
event.ignore()
def dropEvent(self,event):
self.setText(event.mimeData().text())
class application(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
label = my_label("drop there",self)
label.resize(100,100)
label.move(190,65)
label_to_drag = QLabel("drag this",self)
#label_to_drag.setDragEnabled(True) #doesn't work with QLabel
self.show()
def closeEvent(self,event):
message = QMessageBox.question(self,"Message","Quit ?",QMessageBox.Yes | QMessageBox.No,QMessageBox.No)
if message == QMessageBox.Yes:
event.accept()
else:
event.ignore()
app = QApplication(sys.argv)
fenetre = application()
sys.exit(app.exec_())
I wanted to make my first label "draggable",by calling the setDragEnabled(True) method (as i was doing for QLineEdit),and drop it on the second label to change his text. Dropping text from another app is working fine,but I can't find how to drag the QLabel inside my own app...
What did I miss ?
EDIT : modified the code to try to Drag&Drop Images :
class DraggableLabel(QLabel):
def __init__(self,parent,image):
super(QLabel,self).__init__(parent)
self.setPixmap(QPixmap(image))
self.show()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drag_start_position = event.pos()
def mouseMoveEvent(self, event):
if not (event.buttons() & Qt.LeftButton):
return
if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
return
drag = QDrag(self)
mimedata = QMimeData()
mimedata.setText(self.text())
drag.setMimeData(mimedata)
pixmap = QPixmap(self.size())
painter = QPainter(pixmap)
painter.drawPixmap(self.rect(), self.grab())
painter.end()
drag.setPixmap(pixmap)
drag.setHotSpot(event.pos())
drag.exec_(Qt.CopyAction | Qt.MoveAction)
class my_label(QLabel):
def __init__(self,title,parent):
super().__init__(title,parent)
self.setAcceptDrops(True)
def dragEnterEvent(self,event):
if event.mimeData().hasFormat("text/plain"):
print("event accepted")
event.accept()
else:
print("event rejected")
event.ignore()
def dropEvent(self,event):
if event.mimeData().hasImage():
self.setPixmap(QPixmap.fromImage(QImage(event.mimeData().imageData())))
class application(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
label_to_drag = DraggableLabel(self,"index.jpg")
label = my_label("drop there",self)
label.resize(100,100)
label.move(190,65)
self.show()
When I drop the DraggableLabel (displaying the image) on my_label,the event is accepted, but hasImage() always returns false...Is the way I set the image up wrong ?
In the case of QLabel you must create everything from the beginning, for this you can follow the examples of the docs.
In the following example I have placed an example where one class only accepts the drop and the other the drag so that you can see each part and understand better.
import sys
from PyQt5.QtWidgets import QApplication, QLabel, QWidget
from PyQt5.QtGui import QDrag, QPixmap, QPainter, QCursor
from PyQt5.QtCore import QMimeData, Qt
class DraggableLabel(QLabel):
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drag_start_position = event.pos()
def mouseMoveEvent(self, event):
if not (event.buttons() & Qt.LeftButton):
return
if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
return
drag = QDrag(self)
mimedata = QMimeData()
mimedata.setText(self.text())
drag.setMimeData(mimedata)
pixmap = QPixmap(self.size())
painter = QPainter(pixmap)
painter.drawPixmap(self.rect(), self.grab())
painter.end()
drag.setPixmap(pixmap)
drag.setHotSpot(event.pos())
drag.exec_(Qt.CopyAction | Qt.MoveAction)
class DropLabel(QLabel):
def __init__(self, *args, **kwargs):
QLabel.__init__(self, *args, **kwargs)
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
if event.mimeData().hasText():
event.acceptProposedAction()
def dropEvent(self, event):
pos = event.pos()
text = event.mimeData().text()
self.setText(text)
event.acceptProposedAction()
class Widget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
label = DropLabel("drop there",self)
label.setGeometry(190, 65, 100,100)
label_to_drag = DraggableLabel("drag this",self)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
Update:
Do not confuse the QDrag with the QMimeData, if you want to use imageData() you must set it with setImageData() as indicated by the docs:
class DraggableLabel(QLabel):
def __init__(self,parent,image):
super(QLabel,self).__init__(parent)
self.setPixmap(QPixmap(image))
self.show()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drag_start_position = event.pos()
def mouseMoveEvent(self, event):
if not (event.buttons() & Qt.LeftButton):
return
if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
return
drag = QDrag(self)
mimedata = QMimeData()
mimedata.setText(self.text())
mimedata.setImageData(self.pixmap().toImage())
drag.setMimeData(mimedata)
pixmap = QPixmap(self.size())
painter = QPainter(pixmap)
painter.drawPixmap(self.rect(), self.grab())
painter.end()
drag.setPixmap(pixmap)
drag.setHotSpot(event.pos())
drag.exec_(Qt.CopyAction | Qt.MoveAction)
class my_label(QLabel):
def __init__(self,title,parent):
super().__init__(title,parent)
self.setAcceptDrops(True)
def dragEnterEvent(self,event):
if event.mimeData().hasImage():
print("event accepted")
event.accept()
else:
print("event rejected")
event.ignore()
def dropEvent(self,event):
if event.mimeData().hasImage():
self.setPixmap(QPixmap.fromImage(QImage(event.mimeData().imageData())))

Categories

Resources