use mouse drag to change the width of a rectangle? - python

I got this code that helps me draw a rectangle from another SO answer, I'd like to be able to drag the left and right sides of the rectangle to adjust the width of the rectangle, make the rectangle behave in a way similar to how you crop an image on most photo editing software, where you draw the initial area but you have the possibility to adjust the width afterwards to get the crop you want.
the code I have so far:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MyWidget(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(30,30,600,400)
self.begin = QPoint()
self.end = QPoint()
self.show()
def paintEvent(self, event):
qp = QPainter(self)
br = QBrush(QColor(100, 10, 10, 40))
qp.setBrush(br)
qp.drawRect(QRect(self.begin, self.end))
def mousePressEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
# print(f"press begin {self.begin}")
# print(f"press end {self.end}")
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()
print(f"begin {self.begin}")
print(f"end {self.end}")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MyWidget()
window.show()
app.aboutToQuit.connect(app.deleteLater)
sys.exit(app.exec_())

I found the answer to this on a PyQt forum, courtesy of a man by the name of Salem Bream, he answered my question, I thought I'd share it with the SO community.
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
FREE_STATE = 1
BUILDING_SQUARE = 2
BEGIN_SIDE_EDIT = 3
END_SIDE_EDIT = 4
class MyWidget(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(30, 30, 600, 400)
self.begin = QPoint()
self.end = QPoint()
self.state = FREE_STATE
def paintEvent(self, event):
qp = QPainter(self)
br = QBrush(QColor(100, 10, 10, 40))
qp.setBrush(br)
qp.drawRect(QRect(self.begin, self.end))
def mousePressEvent(self, event):
if not self.begin.isNull() and not self.end.isNull():
p = event.pos()
y1, y2 = sorted([self.begin.y(), self.end.y()])
if y1 <= p.y() <= y2:
# 3 resolution, more easy to pick than 1px
if abs(self.begin.x() - p.x()) <= 3:
self.state = BEGIN_SIDE_EDIT
return
elif abs(self.end.x() - p.x()) <= 3:
self.state = END_SIDE_EDIT
return
self.state = BUILDING_SQUARE
self.begin = event.pos()
self.end = event.pos()
self.update()
def applye_event(self, event):
if self.state == BUILDING_SQUARE:
self.end = event.pos()
elif self.state == BEGIN_SIDE_EDIT:
self.begin.setX(event.x())
elif self.state == END_SIDE_EDIT:
self.end.setX(event.x())
def mouseMoveEvent(self, event):
self.applye_event(event)
self.update()
def mouseReleaseEvent(self, event):
self.applye_event(event)
self.state = FREE_STATE
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MyWidget()
window.show()
app.aboutToQuit.connect(app.deleteLater)
sys.exit(app.exec_())

Related

Pyqt: custom frame doesn't detect mouse click under mouseMoveEvent

I have created a custom Frame for a Qgraphicswidget. I have come across two problems, First, one being that clicks are not being detected under MouseMoveEvent, that is event.button() always returns 0 even if there is a mouse click. Second, is that my setCursor() doesn't change the cursor. Here is my code under the custom frame class.
from PyQt5 import QtGui, QtCore
from PyQt5.QtCore import Qt, QRectF, QEvent, QPoint
from PyQt5.QtGui import QPen, QColor, QPainter, QBrush, qRgb, QPolygon
from PyQt5.QtWidgets import *
import sys
class Frame(QFrame):
def __init__(self, parent=None, option=[], margin=0):
super(Frame, self).__init__()
self.parent = parent
self._triangle = QPolygon()
self.options = option
self._margin = margin
self.start_pos = None
# self.parent.setViewport(parent)
self.setStyleSheet('background-color: lightblue')
self.setMouseTracking(True)
self.installEventFilter(self)
self.show()
def update_option(self, option):
self.options = option
def paintEvent(self, event):
super().paintEvent(event)
qp = QPainter(self)
qp.setPen(Qt.white)
qp.setBrush(Qt.gray)
qp.drawPolygon(self._triangle)
def _recalculate_triangle(self):
p = QPoint(self.width() - 20, self.height() - 10)
q = QPoint(self.width() - 10, self.height() - 20)
r = QPoint(self.width() - 10, self.height() - 10)
self._triangle = QPolygon([p, q, r])
self.update()
def resizeEvent(self, event):
self._recalculate_triangle()
super().resizeEvent(event)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
if event.button() == Qt.LeftButton and self._triangle.containsPoint(
event.pos(), Qt.OddEvenFill
):
self.parent.viewport().setCursor(Qt.SizeFDiagCursor)
self.start_pos = event.pos()
# print(self.start_pos)
else:
self.parent.viewport().unsetCursor()
self.start_pos = None
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if self._triangle.containsPoint(event.pos(), Qt.OddEvenFill):
self.parent.viewport().setCursor(Qt.SizeFDiagCursor)
else:
self.parent.viewport().unsetCursor()
self.start_pos = None
if event.button() == Qt.LeftButton:
if event.button() == QtCore.Qt.LeftButton and self.start_pos is not None:
self.parent.viewport().setCursor(Qt.SizeFDiagCursor)
delta = event.pos() - self.start_pos
self.n_resize(self.width()+delta.x(), self.height()+delta.y())
self.start_pos = event.pos()
elif not self._triangle.containsPoint(event.pos(), Qt.OddEvenFill):
self.parent.viewport().unsetCursor()
self.start_pos = None
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self.parent.viewport().unsetCursor()
self.start_pos = None
super().mouseReleaseEvent(event)
def n_resize(self, width, height):
self.resize(width, height)
if __name__ == '__main__':
q = QApplication(sys.argv)
a = Frame()
sys.exit(q.exec())
I have also tried using eventfilter but of no use.
EDIT:
from PyQt5 import QtGui, QtCore
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtGui import QPen, QColor, QPainter, QBrush, QPolygon
from PyQt5.QtWidgets import *
import sys
class graphLayout(QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
self.scene = QGraphicsScene()
self.lines = []
self.draw_grid()
self.set_opacity(0.3)
widget = QGraphicsProxyWidget()
t = stack(self)
t.setFlag(QGraphicsItem.ItemIsMovable)
t.resize(340, 330)
self.scene.addItem(t)
self.setScene(self.scene)
self.show()
def create_texture(self):
image = QtGui.QImage(QtCore.QSize(30, 30), QtGui.QImage.Format_RGBA64)
pen = QPen()
pen.setColor(QColor(189, 190, 191))
pen.setWidth(2)
painter = QtGui.QPainter(image)
painter.setPen(pen)
painter.drawRect(image.rect())
painter.end()
return image
def draw_grid(self):
texture = self.create_texture()
brush = QBrush()
# brush.setColor(QColor('#999'))
brush.setTextureImage(texture) # Grid pattern.
self.scene.setBackgroundBrush(brush)
borderColor = Qt.black
fillColor = QColor('#DDD')
def set_visible(self, visible=True):
for line in self.lines:
line.setVisible(visible)
def delete_grid(self):
for line in self.lines:
self.scene.removeItem(line)
del self.lines[:]
def set_opacity(self, opacity):
for line in self.lines:
line.setOpacity(opacity)
def wheelEvent(self, event):
if event.modifiers() == Qt.ControlModifier:
delta = event.angleDelta().y()
if delta > 0:
self.on_zoom_in()
elif delta < 0:
self.on_zoom_out()
super(graphLayout, self).wheelEvent(event)
def mousePressEvent(self, event):
if event.button() == Qt.MidButton:
self.setCursor(Qt.OpenHandCursor)
self.mousepos = event.localPos()
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
# This helps to pan the area
if event.buttons() == Qt.MidButton:
delta = event.localPos() - self.mousepos
h = self.horizontalScrollBar().value()
v = self.verticalScrollBar().value()
self.horizontalScrollBar().setValue(int(h - delta.x()))
self.verticalScrollBar().setValue(int(v - delta.y()))
self.mousepos = event.localPos()
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self.unsetCursor()
self.mousepos = event.localPos()
super().mouseReleaseEvent(event)
def on_zoom_in(self):
if self.transform().m11() < 3.375:
self.setTransformationAnchor(self.AnchorUnderMouse)
self.scale(1.5, 1.5)
def on_zoom_out(self):
if self.transform().m11() > 0.7:
self.setTransformationAnchor(self.AnchorUnderMouse)
self.scale(1.0 / 1.5, 1.0 / 1.5)
class stack(QGraphicsWidget):
_margin = 0
def __init__(self, parent=None):
super().__init__()
self.options = []
self.gridlayout = parent
graphic_layout = QGraphicsLinearLayout(Qt.Vertical, self)
self.width, self.height = 10, 10
self.outer_container = Frame(parent, self.options, self._margin)
self.outer_container.setContentsMargins(0, 0, 0, 0)
self.setParent(self.outer_container)
layout = QVBoxLayout()
self.headerLayout = QGridLayout()
self.headerLayout.setContentsMargins(2, 0, 0, 0)
self.top_bar = QFrame()
self.top_bar.setFrameShape(QFrame.StyledPanel)
self.top_bar.setLayout(self.headerLayout)
self.top_bar.setContentsMargins(0, 0, 0, 0)
self.top_bar.setMaximumHeight(30)
# self.contentLayout = QVBoxLayout()
self.contentLayout = QFormLayout()
self.contentLayout.setContentsMargins(10, 10, 10, 10)
self.contentLayout.setSpacing(5)
layout.addWidget(self.top_bar)
layout.addLayout(self.contentLayout)
layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
self.outer_container.setContentsMargins(0, 0, 0, 0)
self.setContentsMargins(0, 0, 0, 0)
self.setMaximumSize(400, 800)
self.outer_container.setLayout(layout)
widget = QGraphicsProxyWidget()
widget.setWidget(self.outer_container)
# todo: figure out a way to add top_bar widget
graphic_layout.addItem(widget)
graphic_layout.setSpacing(0)
graphic_layout.setContentsMargins(0, 0, 0, 0)
# widget move and resize note: don't touch any of these
self.__mouseMovePos = None
self._triangle = QPolygon()
self.start_pos = None
def addHeaderWidget(self, widget=None, column=0, bg_color='green'):
self.top_bar.setStyleSheet(f'background-color:{bg_color};')
self.headerLayout.addWidget(widget, 0, column)
class Frame(QFrame):
def __init__(self, parent=None, option=[], margin=0):
super(Frame, self).__init__()
self.parent = parent
self._triangle = QPolygon()
self.options = option
self._margin = margin
self.start_pos = None
# self.parent.setViewport(parent)
self.setStyleSheet('background-color: lightblue')
self.setMouseTracking(True)
self.installEventFilter(self)
self.show()
def update_option(self, option):
self.options = option
def paintEvent(self, event):
super().paintEvent(event)
qp = QPainter(self)
qp.setPen(Qt.white)
qp.setBrush(Qt.gray)
qp.drawPolygon(self._triangle)
def _recalculate_triangle(self):
p = QPoint(self.width() - 20, self.height() - 10)
q = QPoint(self.width() - 10, self.height() - 20)
r = QPoint(self.width() - 10, self.height() - 10)
self._triangle = QPolygon([p, q, r])
self.update()
def resizeEvent(self, event):
self._recalculate_triangle()
super().resizeEvent(event)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
if event.button() == Qt.LeftButton and self._triangle.containsPoint(
event.pos(), Qt.OddEvenFill
):
self.parent.viewport().setCursor(Qt.SizeFDiagCursor)
self.start_pos = event.pos()
# print(self.start_pos)
else:
self.parent.viewport().unsetCursor()
self.start_pos = None
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if self._triangle.containsPoint(event.pos(), Qt.OddEvenFill):
self.parent.viewport().setCursor(Qt.SizeFDiagCursor)
else:
self.parent.viewport().unsetCursor()
self.start_pos = None
if event.button() == Qt.LeftButton:
if event.button() == QtCore.Qt.LeftButton and self.start_pos is not None:
self.parent.viewport().setCursor(Qt.SizeFDiagCursor)
delta = event.pos() - self.start_pos
self.n_resize(self.width()+delta.x(), self.height()+delta.y())
self.start_pos = event.pos()
elif not self._triangle.containsPoint(event.pos(), Qt.OddEvenFill):
self.parent.viewport().unsetCursor()
self.start_pos = None
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self.parent.viewport().unsetCursor()
self.start_pos = None
super().mouseReleaseEvent(event)
def n_resize(self, width, height):
self.resize(width, height)
if __name__ == '__main__':
q = QApplication(sys.argv)
a = graphLayout()
sys.exit(q.exec())
There are various issues on your code, but the base problems are:
mouse button state cannot be retrieved by event.button() in a MouseMove event, and event.buttons() should be used instead: the difference is clear: button() shows the buttons that generate the event (and a mouse move event is obviously not generated by any button), buttons() shows the button state when the event is generated;
events that are not explicitly managed by an object are always propagated to its parent(s), which means that your mouse movements are also possibly processed by the parent widget, then the graphics proxy, the scene, the viewport, the view, etc, and that up to the top level window, until one of the previous objects actually returns True from event() or an event filter; in your case it results in moving the graphics item, since you enabled the ItemIsMovable flag.
I don't know why the cursor is not actually set, but frankly your code is so convoluted that I really cannot find the reason.
Since what you're actually looking for is a way to resize the widget, I suggest you another solution.
While implementing a resizing with custom painting is certainly feasible, in most cases it's well enough to use a QSizeGrip (as already suggested to you in another post), which is a widget that allows resizing top-level windows and is automatically able to understand which "corner" use for the resizing based on its position. Remember that the parent of the QSizeGrip is very important, because it uses it to understand which is its top level window, and, in this case, the "container frame", even if it's in a QGraphicsScene.
Note that QSizeGrip should not be added to a layout, and it should always be manually moved according to its corner position and the size of its parent (unless it's placed on the top left corner), and since you already need custom painting, it's better to subclass it.
from PyQt5 import QtCore, QtGui, QtWidgets
class SizeGrip(QtWidgets.QSizeGrip):
def __init__(self, parent):
super().__init__(parent)
parent.installEventFilter(self)
self.setFixedSize(30, 30)
self.polygon = QtGui.QPolygon([
QtCore.QPoint(10, 20),
QtCore.QPoint(20, 10),
QtCore.QPoint(20, 20),
])
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.Resize:
geo = self.rect()
geo.moveBottomRight(source.rect().bottomRight())
self.setGeometry(geo)
return super().eventFilter(source, event)
def paintEvent(self, event):
qp = QtGui.QPainter(self)
qp.setPen(QtCore.Qt.white)
qp.setBrush(QtCore.Qt.gray)
qp.drawPolygon(self.polygon)
class Container(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.sizeGrip = SizeGrip(self)
self.startPos = None
layout = QtWidgets.QVBoxLayout(self)
layout.setContentsMargins(6, 6, 6, 30)
self.setStyleSheet('''
Container {
background: lightblue;
border: 0px;
border-radius: 4px;
}
''')
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.startPos = event.pos()
def mouseMoveEvent(self, event):
if self.startPos:
self.move(self.pos() + (event.pos() - self.startPos))
def mouseReleaseEvent(self, event):
self.startPos = None
class GraphicsRoundedFrame(QtWidgets.QGraphicsProxyWidget):
def __init__(self):
super().__init__()
self.container = Container()
self.setWidget(self.container)
def addWidget(self, widget):
self.container.layout().addWidget(widget)
def paint(self, qp, opt, widget):
qp.save()
p = QtGui.QPainterPath()
p.addRoundedRect(self.boundingRect().adjusted(0, 0, -.5, -.5), 4, 4)
qp.setClipPath(p)
super().paint(qp, opt, widget)
qp.restore()
class View(QtWidgets.QGraphicsView):
def __init__(self):
super().__init__()
scene = QtWidgets.QGraphicsScene()
self.setScene(scene)
self.setRenderHints(QtGui.QPainter.Antialiasing)
scene.setSceneRect(0, 0, 1024, 768)
texture = QtGui.QImage(30, 30, QtGui.QImage.Format_ARGB32)
qp = QtGui.QPainter(texture)
qp.setBrush(QtCore.Qt.white)
qp.setPen(QtGui.QPen(QtGui.QColor(189, 190, 191), 2))
qp.drawRect(texture.rect())
qp.end()
scene.setBackgroundBrush(QtGui.QBrush(texture))
testFrame = GraphicsRoundedFrame()
scene.addItem(testFrame)
testFrame.addWidget(QtWidgets.QLabel('I am a label'))
testFrame.addWidget(QtWidgets.QPushButton('I am a button'))
import sys
app = QtWidgets.QApplication(sys.argv)
w = View()
w.show()
sys.exit(app.exec_())

Erasing pen on a canvas

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())

PyQT5: How to interactively paint on image within QLabel Widget? [duplicate]

This question already has an answer here:
Drawing with a brush
(1 answer)
Closed 4 years ago.
Python 3, latest version of PyQt5 on Mac OS Mojave
I want a PyQt5 program in which the user could paint connected dots on an image (click distinctively and the points are automatically connected). It is important that I can only draw on the image in the QLabel widget (or an alternative widget) and not over the entire main window.
I can plot the image and get the the coordinates of the previous two clicks but when I want to paint on the image it happens underneath the image. Further I have troubles in getting the coordinates as input for my paintevent.
class Example(QWidget):
def __init__(self):
super().__init__()
title = "Darcy"
top = 400
left = 400
width = 550
height = 600
self.clickcount = 0
self.x = 0
self.y = 0
self.setWindowTitle(title)
self.setGeometry(top,left, width, height)
self.initUI()
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
self.drawLines(qp)
qp.end()
def drawLines(self, qp):
pen = QPen(Qt.black, 2, Qt.SolidLine)
qp.setPen(pen)
qp.drawLine(20, 40, 250, 40)
def initUI(self):
self.map = QLabel()
Im = QPixmap("GM_loc.png")
Im = Im.scaled(450,450)
self.map.setPixmap(Im)
self.loc = QLabel()
self.test = QLabel()
self.map.mousePressEvent = self.getPos
#organize in grid
grid = QGridLayout()
grid.setSpacing(10)
grid.addWidget(self.map, 0, 0)
grid.addWidget(self.loc,1,0)
grid.addWidget(self.test,2,0)
self.setLayout(grid)
self.show()
def getPos(self , event):
self.clickcount += 1
self.x_old = self.x
self.y_old = self.y
self.x = event.pos().x()
self.y = event.pos().y()
self.loc.setText("x = "+str(self.x)+" & y= "+str(self.y)+" & old x = " + str(self.x_old) + " & old y = " + str(self.y_old))
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
Thanks in advance!
PS I am a rookie in PyQt5 so any hints in more efficient code are more than welcome!
Try it:
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
class MyScribbling(QMainWindow):
def __init__(self):
super().__init__()
self.penOn = QAction(QIcon('Image/ok.png'), 'ON drawing', self)
self.penOn.triggered.connect(self.drawingOn)
self.penOff = QAction(QIcon('Image/exit.png'), 'OFF drawing', self)
self.penOff.triggered.connect(self.drawingOff)
toolbar = self.addToolBar('Tools')
toolbar.addAction(self.penOn)
toolbar.addAction(self.penOff)
self.scribbling = False
self.myPenColor = Qt.red
self.myPenWidth = 3
self.lastPoint = QPoint()
self.image = QPixmap("Image/picture.png")
self.setFixedSize(600, 600)
self.resize(self.image.width(), self.image.height())
self.setWindowTitle("drawing On / Off")
def paintEvent(self, event):
painter = QPainter(self)
painter.drawPixmap(self.rect(), self.image)
def mousePressEvent(self, event):
if (event.button() == Qt.LeftButton) and self.scribbling:
self.lastPoint = event.pos()
def mouseMoveEvent(self, event):
if (event.buttons() & Qt.LeftButton) and self.scribbling:
painter = QPainter(self.image)
painter.setPen(QPen(self.myPenColor, self.myPenWidth,
Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
painter.drawLine(self.lastPoint, event.pos())
self.lastPoint = event.pos()
self.update()
def drawingOn(self):
self.scribbling = True
def drawingOff(self):
self.scribbling = False
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MyScribbling()
ex.show()
sys.exit(app.exec_())

How to draw a rectangle and adjust its shape by drag and drop in PyQt5

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()

Paint ticks on custom QProgressBar in Pyside

I'm trying to paint some ticks in my custom progressbar but I'm not clear on why the line isn't showing up at all?
import sys
import os
sys.path.append('Z:\\pipeline\\site-packages')
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from PySide import QtGui, QtCore
class QProgressBarPro(QtGui.QProgressBar):
progressClicked = QtCore.Signal()
progressChanging = QtCore.Signal()
progressChanged = QtCore.Signal()
def __init__(self, parent=None):
super(QProgressBarPro, self).__init__(parent)
self.default_value = 50.0
self.lmb_pressed = False
self.setFormat('%p')
self.setRange(0.0, 100.0)
self.stepEnabled = True
self.step = 5
self.setToolTip('<strong>Press+Hold+Ctrl</strong> for percise values<br><strong>Right-Click</strong> to reset default value')
def step_round(self, x, base=5):
return int(base * round(float(x)/base))
def set_value_from_cursor(self, xpos):
width = self.frameGeometry().width()
percent = float(xpos) / width
val = self.maximum() * percent
if self.stepEnabled:
modifiers = QtGui.QApplication.keyboardModifiers()
if modifiers != QtCore.Qt.ControlModifier:
val = self.step_round(val, self.step)
self.setValue(val)
def mousePressEvent(self, event):
self.progressClicked.emit()
mouse_button = event.button()
if mouse_button == QtCore.Qt.RightButton:
self.setValue(self.default_value)
else:
xpos = event.pos().x()
self.set_value_from_cursor(xpos)
self.lmb_pressed = True
self.progressChanging.emit()
def mouseReleaseEvent(self, event):
self.lmb_pressed = False
self.progressChanged.emit()
def mouseMoveEvent(self, event):
if self.lmb_pressed:
xpos = event.pos().x()
self.set_value_from_cursor(xpos)
self.progressChanging.emit()
def paintEvent(self, event):
painter = QtGui.QPainter()
painter.drawLine(10, 0, 10, 10)
QtGui.QProgressBar.paintEvent(self, event)
# DEMO
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.ui_progress = QProgressBarPro()
self.ui_progress.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.ui_progress.setValue(10)
gdl = QtGui.QVBoxLayout()
gdl.addWidget(self.ui_progress)
self.setLayout(gdl)
self.resize(300, 300)
self.setWindowTitle('Tooltips')
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You need to change your paintEvent function.
I wrote a first approach that provides same result as in your image:
def paintEvent(self, event):
QtGui.QProgressBar.paintEvent(self, event)
painter = QtGui.QPainter(self)
brush = QtGui.QBrush(QtCore.Qt.SolidPattern)
# Set gray color
brush.setColor(QtGui.QColor(204,204,204))
painter.setPen(QtGui.QPen(brush, 2, QtCore.Qt.SolidLine,QtCore.Qt.RoundCap))
#print(str(self.width())+","+str(self.height()))
progressbarwidth = self.width()
progressbarheight = self.height()
## Drawing one vertical line each 1/5
painter.drawLine(progressbarwidth*1/5, 0, progressbarwidth*1/5, progressbarheight)
painter.drawLine(progressbarwidth*2/5, 0, progressbarwidth*2/5, progressbarheight)
painter.drawLine(progressbarwidth*3/5, 0, progressbarwidth*3/5, progressbarheight)
painter.drawLine(progressbarwidth*4/5, 0, progressbarwidth*4/5, progressbarheight)
The achieved outcome is shown here.

Categories